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

342 lines
17 KiB
Plaintext

@using System.Net.Http.Headers
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject ISnackbar Snackbar
@inject IHttpClientFactory HttpClientFactory
@inject IDialogService Dialog
<div class="d-flex w-100 h-100 flex-column">
<div class="d-flex flex-row justify-content-between w-100 mb-3">
<MudText Typo="Typo.h5" Class="d-flex align-items-center">Element Models</MudText>
<div class="d-flex flex-row pe-2">
<MudTooltip RootClass="d-flex align-items-center" Text="Create" Placement="Placement.Bottom" Color="Color.Primary" Arrow="true">
<MudIconButton Icon="@Icons.Material.Filled.Add" Variant="Variant.Text" Color="Color.Primary" Size="Size.Small" OnClick="OpenCreateModel"></MudIconButton>
</MudTooltip>
<MudTooltip RootClass="d-flex align-items-center" Text="Edit" Placement="Placement.Bottom" Color="Color.Info" Arrow="true">
<MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Small" Color="Color.Info" OnClick="OpenUpdateElementModel" />
</MudTooltip>
<MudTooltip RootClass="d-flex align-items-center" Text="Delete" Placement="Placement.Bottom" Color="Color.Secondary" Arrow="true">
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Secondary" OnClick="DeleteElementModel" />
</MudTooltip>
</div>
</div>
<div class="d-flex w-100 flex-grow-1 overflow-x-hidden overflow-y-auto">
<MudTreeView Class="d-flex w-100 h-100 flex-column" T="ElementModelDto" Hover SelectedValueChanged="ModelSelectedChanged" SelectedValue="ElementModelSelected">
@foreach (var model in ElementModels)
{
<MudTreeViewItem Value="@model" CanExpand="false" Style="border-bottom: 0.5px solid rgba(217, 217, 217, 0.5); "
Text="@model.Name" EndText="@($"{model.Width}x{model.Height}")" EndTextTypo="Typo.caption" />
}
</MudTreeView>
</div>
</div>
<MudDialog @bind-Visible="@CreateModelIsVisible" DefaultFocus="DefaultFocus.FirstChild">
<TitleContent>
<MudText Typo="Typo.h5">Create Element Model</MudText>
</TitleContent>
<DialogContent>
<div class="d-flex flex-column" style="max-height: 700px; overflow-y: auto">
<MudTextField Class="my-2"
@bind-Value="ElementModelCreateModel.Name" T="string"
Label="Name" Required="true" RequiredError="Name is required!"
Counter="18" Variant="Variant.Outlined"
Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
<div class="d-flex flex-row">
<MudNumericField Class="me-2" @bind-Value="ElementModelCreateModel.Width" Label="Width" Step="0.1" Min="0" Variant="Variant.Outlined" />
<MudNumericField Class="ms-2" @bind-Value="ElementModelCreateModel.Height" Label="Height" Step="0.1" Min="0" Variant="Variant.Outlined" />
</div>
<div class="d-flex flex-row justify-content-around">
<div class="d-flex flex-column me-2">
<div class="d-flex flex-row align-items-center justify-content-center">
<InputFile id="fileElementModelImage1Create" OnChange="ElementModelCreateImage1Changed" accept=".png" hidden />
<MudText>Open</MudText>
<MudIconButton Variant="Variant.Text" Color="Color.Primary" Icon="@Icons.Material.Filled.CloudUpload" for="fileElementModelImage1Create" HtmlTag="label" />
</div>
<div class="d-flex">
<div class="model-create-item">
<img alt="Element Model Image Open" src="@ProjectionImageFilePreview1" style="image-rendering: pixelated;" />
</div>
</div>
</div>
<div class="d-flex flex-column">
<div class="d-flex flex-row align-items-center justify-content-center">
<InputFile id="fileElementModelImage2Create" OnChange="ElementModelCreateImage2Changed" accept=".png" hidden />
<MudText>Close</MudText>
<MudIconButton Variant="Variant.Text" Color="Color.Primary" Icon="@Icons.Material.Filled.CloudUpload" for="fileElementModelImage2Create" HtmlTag="label" />
</div>
<div class="d-flex">
<div class="model-create-item">
<img alt=" Element Model Image Close" src="@ProjectionImageFilePreview2" style="image-rendering: pixelated;" />
</div>
</div>
</div>
</div>
</div>
</DialogContent>
<DialogActions>
<MudButton Color="Color.Default" Variant="Variant.Filled" OnClick="(() => CreateModelIsVisible = false)">Cancel</MudButton>
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="CreateElementModel" Disabled="string.IsNullOrEmpty(ElementModelCreateModel.Name)">Create</MudButton>
</DialogActions>
</MudDialog>
<MudDialog @bind-Visible="@UpdateElementModelVisible" DefaultFocus="DefaultFocus.FirstChild">
<TitleContent>
<MudText Typo="Typo.h5">Update Element Model</MudText>
</TitleContent>
<DialogContent>
<div class="d-flex flex-column" style="max-height: 700px; overflow-y: auto">
<MudTextField Class="my-2"
@bind-Value="ElementModelUpdateModel.Name" T="string"
Label="Name" Required="true" RequiredError="Name is required!"
Counter="18" Variant="Variant.Outlined"
Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
<div class="d-flex flex-row">
<MudNumericField Class="me-2" @bind-Value="ElementModelUpdateModel.Width" Label="Width" Step="0.1" Variant="Variant.Outlined" />
<MudNumericField Class="ms-2" @bind-Value="ElementModelUpdateModel.Height" Label="Height" Step="0.1" Variant="Variant.Outlined" />
</div>
</div>
</DialogContent>
<DialogActions>
<MudButton Color="Color.Default" Variant="Variant.Filled" OnClick="(() => UpdateElementModelVisible = false)">Cancel</MudButton>
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="UpdateElementModel" Disabled="string.IsNullOrEmpty(ElementModelUpdateModel.Name)">Update</MudButton>
</DialogActions>
</MudDialog>
@code {
[Parameter]
public List<ElementModelDto> ElementModels { get; set; } = [];
[Parameter]
public MapInfoDto MapInfo { get; set; } = new();
[Parameter]
public EventCallback<ElementModelDto?> ElementModelSelectChanged { get; set; }
private string? txtSearch { get; set; }
private bool CreateModelIsVisible;
private ElementModelCreateModel ElementModelCreateModel = new();
private IBrowserFile? ElementModelCreateImage1 { get; set; }
private string? ProjectionImageFilePreview1 { get; set; }
private IBrowserFile? ElementModelCreateImage2 { get; set; }
private string? ProjectionImageFilePreview2 { get; set; }
private ElementModelDto? ElementModelSelected = null;
private bool UpdateElementModelVisible;
private ElementModelUpdateModel ElementModelUpdateModel = new();
private HttpClient Http = default!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
Http = HttpClientFactory.CreateClient("MapManagerAPI");
}
private async Task DeleteElementModel()
{
if (ElementModelSelected is null) return;
var parameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Content, $"Tất cả các Element thuộc model này cũng sẽ bị xóa! Bạn có chắc chắn muốn xóa element model {ElementModelSelected.Name} đi không?" },
{ x => x.ConfirmText, "Delete" },
{ x => x.Color, Color.Secondary }
};
var ConfirmDelete = await Dialog.ShowAsync<ConfirmDialog>("Xoá Element Model", 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/ElementModels/{ElementModelSelected.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 elementmodel = ElementModels.FirstOrDefault(m => m.Id == ElementModelSelected.Id);
if (elementmodel is not null)
{
ElementModels.Remove(elementmodel);
await ElementModelSelectChanged.InvokeAsync(null);
ElementModelSelected = null;
Snackbar.Add($"Xóa element model thành công.", Severity.Success);
}
else Snackbar.Add($"Element Model không tồn tại.", Severity.Warning);
}
StateHasChanged();
}
}
private void OpenCreateModel()
{
ElementModelCreateModel.Name = "";
ElementModelCreateModel.MapId = MapInfo.Id;
ProjectionImageFilePreview1 = "/images/Image-not-found.png";
ProjectionImageFilePreview2 = "/images/Image-not-found.png";
ElementModelCreateImage1 = null;
ElementModelCreateImage2 = null;
CreateModelIsVisible = true;
StateHasChanged();
}
private async Task CreateElementModel()
{
try
{
if (ElementModelCreateImage1 is null || string.IsNullOrEmpty(ProjectionImageFilePreview1))
{
Snackbar.Add("Ảnh model chưa được chọn.", Severity.Warning);
return;
}
if (ElementModelCreateImage2 is null || string.IsNullOrEmpty(ProjectionImageFilePreview2))
{
Snackbar.Add("Ảnh model chưa được chọn.", Severity.Warning);
return;
}
if (string.IsNullOrEmpty(ElementModelCreateModel.Name))
{
Snackbar.Add("Name không được để trống.", Severity.Warning);
return;
}
var nameInvalid = MapEditorHelper.NameChecking(ElementModelCreateModel.Name);
if (!nameInvalid.IsSuccess)
{
Snackbar.Add(nameInvalid.returnStr, Severity.Warning);
return;
}
using var content = new MultipartFormDataContent
{
{ new StringContent(ElementModelCreateModel.MapId.ToString()), nameof(ElementModelCreateModel.MapId) },
{ new StringContent(ElementModelCreateModel.Name ?? String.Empty), nameof(ElementModelCreateModel.Name) },
{ new StringContent(ElementModelCreateModel.Width.ToString()), nameof(ElementModelCreateModel.Width) },
{ new StringContent(ElementModelCreateModel.Height.ToString()), nameof(ElementModelCreateModel.Height) },
};
var fileContent1 = new StreamContent(ElementModelCreateImage1.OpenReadStream());
fileContent1.Headers.ContentType = new MediaTypeHeaderValue(ElementModelCreateImage1.ContentType);
content.Add(fileContent1, "imageOpen", ElementModelCreateImage1.Name);
var fileContent2 = new StreamContent(ElementModelCreateImage2.OpenReadStream());
fileContent2.Headers.ContentType = new MediaTypeHeaderValue(ElementModelCreateImage2.ContentType);
content.Add(fileContent2, "imageClose", ElementModelCreateImage2.Name);
var response = await (await Http.PostAsync("api/ElementModels", content)).Content.ReadFromJsonAsync<MessageResult<ElementModelDto>>();
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 if (response.Data is null) Snackbar.Add("Hệ thống không thể tạo Element model này", Severity.Error);
else
{
ElementModels.Add(response.Data);
Snackbar.Add($"Tạo Element model {response.Data.Name} thành công.", Severity.Success);
}
CreateModelIsVisible = false;
StateHasChanged();
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
return;
}
}
private async Task ElementModelCreateImage1Changed(InputFileChangeEventArgs e)
{
ElementModelCreateImage1 = e.File;
if (ElementModelCreateImage1 is not null)
{
var buffers = new byte[ElementModelCreateImage1.Size];
var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
await using var fs = new FileStream(path, FileMode.Create);
await ElementModelCreateImage1.OpenReadStream(ElementModelCreateImage1.Size).CopyToAsync(fs);
fs.Position = 0;
await fs.ReadAsync(buffers);
fs.Close();
File.Delete(path);
ProjectionImageFilePreview1 = $"data:{ElementModelCreateImage1.ContentType};base64,{Convert.ToBase64String(buffers)}";
StateHasChanged();
}
}
private async Task ElementModelCreateImage2Changed(InputFileChangeEventArgs e)
{
ElementModelCreateImage2 = e.File;
if (ElementModelCreateImage2 is not null)
{
var buffers = new byte[ElementModelCreateImage2.Size];
var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
await using var fs = new FileStream(path, FileMode.Create);
await ElementModelCreateImage2.OpenReadStream(ElementModelCreateImage2.Size).CopyToAsync(fs);
fs.Position = 0;
await fs.ReadAsync(buffers);
fs.Close();
File.Delete(path);
ProjectionImageFilePreview2 = $"data:{ElementModelCreateImage2.ContentType};base64,{Convert.ToBase64String(buffers)}";
StateHasChanged();
}
}
private async Task ModelSelectedChanged(ElementModelDto model)
{
ElementModelSelected = model;
var elModel = await Http.GetFromJsonAsync<MessageResult<ElementModelDto>>($"api/ElementModels/{ElementModelSelected.Id}");
if (elModel is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
else if (!elModel.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {elModel.Message}", Severity.Error);
else await ElementModelSelectChanged.InvokeAsync(elModel.Data);
}
private void OpenUpdateElementModel()
{
if (ElementModelSelected is null) return;
ElementModelUpdateModel.Id = ElementModelSelected.Id;
ElementModelUpdateModel.Name = ElementModelSelected.Name;
ElementModelUpdateModel.Width = ElementModelSelected.Width;
ElementModelUpdateModel.Height = ElementModelSelected.Height;
ElementModelUpdateModel.MapId = ElementModelSelected.MapId;
ElementModelUpdateModel.Content = ElementModelSelected.Content;
UpdateElementModelVisible = true;
StateHasChanged();
}
private async Task UpdateElementModel()
{
var result = await (await Http.PutAsJsonAsync($"api/ElementModels", new ElementModelUpdateModel()
{
Id = ElementModelUpdateModel.Id,
Name = ElementModelUpdateModel.Name,
Width = ElementModelUpdateModel.Width,
Height = ElementModelUpdateModel.Height,
MapId = ElementModelUpdateModel.MapId,
Content = ElementModelUpdateModel.Content,
})).Content.ReadFromJsonAsync<MessageResult<ElementModelDto>>();
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
{
var elementModel = ElementModels.FirstOrDefault(m => m.Id == result.Data.Id);
if (elementModel is not null)
{
elementModel.Name = result.Data.Name;
elementModel.Width = result.Data.Width;
elementModel.Height = result.Data.Height;
}
Snackbar.Add("Cập nhật thành công", Severity.Success);
}
UpdateElementModelVisible = false;
StateHasChanged();
}
}