RobotNet/RobotNet.WebApp/Maps/Components/Element/MapElement.razor
2025-10-15 15:15:53 +07:00

289 lines
12 KiB
Plaintext

@inject IHttpClientFactory HttpClientFactory
@inject ISnackbar Snackbar
@inject IDialogService Dialog
@inject NavigationManager Nav
<div class="d-flex w-100 h-100 flex-column ps-2">
<div class="d-flex flex-row justify-content-between w-100 pb-1">
<MudText Typo="Typo.h5" Class="d-flex align-items-start">Elements</MudText>
<MudSpacer />
<MudTextField @bind-Value="txtSearch" Placeholder="Search" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium"
Class="mt-0" Variant="Variant.Outlined" Margin="Margin.Dense" TextChanged="TextSearchChanged" />
<MudTooltip RootClass="d-flex align-items-center" Text="Exit" Placement="Placement.Bottom" Color="Color.Info" Arrow="true">
<MudIconButton Class="mx-2 mb-1" Icon="@Icons.Material.Filled.KeyboardReturn" Variant="Variant.Filled" Color="Color.Info" OnClick="@(() => Nav.NavigateTo($"/navigation-maps"))"></MudIconButton>
</MudTooltip>
</div>
<div class="d-flex w-100" style="height: 93%">
<MudTable Class="w-100" T="ElementDto" @ref="Table" Items="@ElementsShow" Dense Hover ReadOnly Striped Elevation="0" FixedHeader="true"
Height="92%" RowClass="cursor-pointer" ServerData="ReloadData" Loading="@IsLoading"
RowClassFunc="@SelectedRowClassFunc" OnRowClick="RowClickEvent">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Model</MudTh>
<MudTh>IsOpen</MudTh>
<MudTh>OffsetX</MudTh>
<MudTh>OffsetY</MudTh>
<MudTh>NodeId</MudTh>
<MudTh></MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd DataLabel="Model">@context.ModelName</MudTd>
<MudTd DataLabel="IsOpen">@context.IsOpen</MudTd>
<MudTd DataLabel="OffsetX">@context.OffsetX</MudTd>
<MudTd DataLabel="OffsetY">@context.OffsetY</MudTd>
<MudTd DataLabel="Node">@context.NodeId</MudTd>
<MudTd>
<div class="d-flex flex-row-reverse">
<MudMenu Icon="@Icons.Material.Filled.MoreVert" AnchorOrigin="Origin.BottomCenter">
<MudMenuItem OnClick="(() => OpenEditElement(context))" Icon="@Icons.Material.Filled.Edit" IconColor="Color.Info">Edit</MudMenuItem>
<MudMenuItem OnClick="(async () => await Delete(context))" Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error">Delete</MudMenuItem>
</MudMenu>
</div>
</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
</div>
</div>
<style>
.selected {
background-color: #3399ff !important;
}
.selected > td {
color: white !important;
}
.selected > td .mud-input {
color: white !important;
}
</style>
<MudDialog @bind-Visible="updateElementVisble">
<TitleContent>
<MudText Typo="Typo.h6">
Update Element @ElementUpdateModel.Name
</MudText>
</TitleContent>
<DialogContent>
<div class="d-flex flex-column" style="max-height: 700px; overflow-y: auto; width: 300px">
<MudTextField T="string" Label="Name" @bind-value="ElementUpdateModel.Name" Variant="Variant.Outlined"
Required="true" RequiredError="Name is required!"
Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
<MudNumericField T="double" Label="OffsetX" @bind-value="ElementUpdateModel.OffsetX" Variant="Variant.Outlined" Step="0.1" />
<MudNumericField T="double" Label="OffsetY" @bind-value="ElementUpdateModel.OffsetY" Variant="Variant.Outlined" Step="0.1" />
<MudCheckBox Size="Size.Small" T="bool" LabelPlacement="Placement.Start" Label="IsOpen" @bind-value="ElementUpdateModel.IsOpen" Color="Color.Success"
UncheckedColor="Color.Default" />
</div>
</DialogContent>
<DialogActions>
<MudButton OnClick="(() => updateElementVisble = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
<MudButton OnClick="UpdateElement" Variant="Variant.Filled" Color="Color.Primary" Class="px-10">Update</MudButton>
</DialogActions>
</MudDialog>
@code {
[Parameter]
public EventCallback<ElementDto?> ElementSelectChanged { get; set; }
private List<ElementDto> Elements { get; set; } = [];
private List<ElementDto> ElementsShow { get; set; } = [];
private string txtSearch = "";
private bool IsLoading;
private MudTable<ElementDto> Table = default!;
private int selectedRowNumber = -1;
private bool updateElementVisble;
private ElementUpdateModel ElementUpdateModel = new();
private HttpClient Http = default!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
Http = HttpClientFactory.CreateClient("MapManagerAPI");
}
private bool FilterFunc(ElementDto element)
{
if (string.IsNullOrWhiteSpace(txtSearch))
return true;
if (element.Name.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
private Task<TableData<ElementDto>> ReloadData(TableState state, CancellationToken _)
{
var robots = new List<ElementDto>();
ElementsShow.Clear();
Elements.ForEach(robot =>
{
if (FilterFunc(robot)) robots.Add(robot);
});
ElementsShow = robots.Skip(state.Page * state.PageSize).Take(state.PageSize).ToList();
return Task.FromResult(new TableData<ElementDto>() { TotalItems = robots.Count(), Items = ElementsShow });
}
private async Task LoadElements(Guid modelId)
{
IsLoading = true;
Elements.Clear();
StateHasChanged();
var elements = await Http.GetFromJsonAsync<MessageResult<IEnumerable<ElementDto>>>($"api/Elements/{modelId}");
if (elements is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!elements.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {elements.Message}", Severity.Error);
else if (elements.Data is null || !elements.Data.Any())
{
Snackbar.Add("Không có element nào trong model này.", Severity.Warning);
ElementsShow.Clear();
Table?.ReloadServerData();
}
else
{
Elements.AddRange(elements.Data.OrderBy(el => el.Name));
Table?.ReloadServerData();
}
IsLoading = false;
StateHasChanged();
}
public async Task SetData(ElementModelDto? model)
{
selectedRowNumber = -1;
Table.SetSelectedItem(null);
_ = ElementSelectChanged.InvokeAsync(null);
if (model is null)
{
Elements.Clear();
await Table.ReloadServerData();
}
else await LoadElements(model.Id);
}
private void RowClickEvent(TableRowClickEventArgs<ElementDto> tableRowClickEventArgs) { }
private string SelectedRowClassFunc(ElementDto element, int rowNumber)
{
if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && !Table.SelectedItem.Equals(element))
{
return string.Empty;
}
else if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
{
return "selected";
}
else if (Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
{
selectedRowNumber = rowNumber;
_ = ElementSelectChanged.InvokeAsync(element);
return "selected";
}
else
{
return string.Empty;
}
}
private void TextSearchChanged(string text)
{
txtSearch = text;
Table?.ReloadServerData();
}
private async Task Delete(ElementDto element)
{
var parameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Content, $"Bạn có chắc chắn muốn xóa element {element.Name} đi không?" },
{ x => x.ConfirmText, "Delete" },
{ x => x.Color, Color.Secondary }
};
var ConfirmDelete = await Dialog.ShowAsync<ConfirmDialog>("Xoá Element", parameters);
var result = await ConfirmDelete.Result;
if (result is not null && result.Data is not null && bool.TryParse(result.Data.ToString(), out bool data) && data)
{
var response = await Http.DeleteFromJsonAsync<MessageResult>($"api/Elements/{element.Id}");
if (response is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!response.IsSuccess) Snackbar.Add(response.Message ?? "Lỗi chưa xác định.", Severity.Error);
else
{
var elementData = Elements.FirstOrDefault(m => m.Id == element.Id);
if (elementData is not null)
{
Elements.Remove(elementData);
await Table.ReloadServerData();
await ElementSelectChanged.InvokeAsync(null);
selectedRowNumber = -1;
Snackbar.Add($"Xóa element thành công.", Severity.Success);
}
else Snackbar.Add($"Element không tồn tại.", Severity.Warning);
}
StateHasChanged();
}
}
private void OpenEditElement(ElementDto model)
{
ElementUpdateModel.Id = model.Id;
ElementUpdateModel.Name = model.Name;
ElementUpdateModel.IsOpen = model.IsOpen;
ElementUpdateModel.OffsetX = model.OffsetX;
ElementUpdateModel.OffsetY = model.OffsetY;
ElementUpdateModel.Content = model.Content;
updateElementVisble = true;
StateHasChanged();
}
private async Task UpdateElement()
{
var selectedElement = Elements.FirstOrDefault(e => e.Id == ElementUpdateModel.Id);
if (selectedElement is null) return;
var result = await (await Http.PutAsJsonAsync($"api/Elements", new ElementUpdateModel()
{
Id = ElementUpdateModel.Id,
Name = ElementUpdateModel.Name,
IsOpen = ElementUpdateModel.IsOpen,
OffsetX = ElementUpdateModel.OffsetX,
OffsetY = ElementUpdateModel.OffsetY,
Content = ElementUpdateModel.Content,
})).Content.ReadFromJsonAsync<MessageResult<ElementDto>>();
if (result == null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!result.IsSuccess) Snackbar.Add(result.Message, Severity.Error);
else if (result.Data is null) Snackbar.Add("Lỗi dữ liệu", Severity.Error);
else
{
selectedElement.Name = result.Data.Name;
selectedElement.OffsetX = result.Data.OffsetX;
selectedElement.OffsetY = result.Data.OffsetY;
selectedElement.IsOpen = result.Data.IsOpen;
Snackbar.Add("Cập nhật thành công", Severity.Success);
}
updateElementVisble = false;
StateHasChanged();
}
public void UpdateProperty(ElementDto model)
{
var element = Elements.FirstOrDefault(e => e.Id == model.Id);
if (element is null) return;
element.Name = model.Name;
element.OffsetX = model.OffsetX;
element.OffsetY = model.OffsetY;
element.IsOpen = model.IsOpen;
element.Content = model.Content;
StateHasChanged();
}
}