490 lines
21 KiB
Plaintext
490 lines
21 KiB
Plaintext
@using System.Text.Json
|
|
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
|
@using RobotNet.WebApp.Maps.Components.Editor.Element
|
|
|
|
@inject ISnackbar Snackbar
|
|
@inject IHttpClientFactory HttpClientFactory
|
|
@inject IDialogService Dialog
|
|
|
|
@foreach (var node in Models)
|
|
{
|
|
<Node Model="node" ShowName="@ShowName" OnClick="NodeClickChanged" DoubleClick="OnNodeDoubleClick" EditorState="@EditorState" />
|
|
}
|
|
|
|
@foreach (var element in Elements)
|
|
{
|
|
<Element Model="@element" ShowName="@ShowName" EditorState="EditorState" DoubleClick="ElementDoubleClick" />
|
|
}
|
|
|
|
<MudDialog @bind-Visible="updateNodeVisible">
|
|
<TitleContent>
|
|
<MudText Typo="Typo.h6">
|
|
Update node @UpdateModel.Id
|
|
</MudText>
|
|
</TitleContent>
|
|
<DialogContent>
|
|
<div class="d-flex flex-row">
|
|
<div class="d-flex flex-column">
|
|
<div class="mb-2">
|
|
<MudTextField @bind-Value="UpdateModel.Name" Label="Name" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
|
|
</div>
|
|
<div class="mb-2">
|
|
<MudNumericField @bind-Value="UpdateModel.X" Label="X" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
|
|
</div>
|
|
<div class="mb-2">
|
|
<MudNumericField @bind-Value="UpdateModel.Y" Label="Y" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
|
|
</div>
|
|
<div class="mb-2">
|
|
<MudNumericField @bind-Value="UpdateModel.Theta" Label="Theta" AdornmentText="Degree" Max="180" Min="-180" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
|
|
</div>
|
|
<div class="mb-2">
|
|
<MudNumericField @bind-Value="UpdateModel.AllowedDeviationXy" Label="AllowedDeviationXy" Step="0.01" Min="0" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
|
|
</div>
|
|
<div>
|
|
<MudNumericField @bind-Value="UpdateModel.AllowedDeviationTheta" Label="AllowedDeviationTheta" Step="1" Min="0" Max="180" ShrinkLabel Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly=@(MapIsActive) />
|
|
</div>
|
|
</div>
|
|
<div class="d-flex flex-column ms-2 flex-grow-1">
|
|
<div class="d-flex flex-row mb-5">
|
|
<MudSelect @bind-Value=ActionSelected Margin="Margin.Dense" T="ActionDto" Label="Action" Variant="Variant.Outlined" Class="w-100" AnchorOrigin="Origin.BottomLeft">
|
|
@foreach (var action in Actions)
|
|
{
|
|
<MudSelectItem Value="@action">@action.Name</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
<MudFab Class="ms-2" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Add" Size="Size.Medium" OnClick="AddAction" />
|
|
</div>
|
|
<div class="paper-action">
|
|
@foreach (var actionId in UpdateModel.Actions)
|
|
{
|
|
<div class="m-1" style="height: fit-content">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Info" Style="text-transform:none" OnClick="(() => DeleteAction(actionId))">
|
|
@(Actions.FirstOrDefault(ac => ac.Id == actionId)?.Name)
|
|
</MudButton>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<MudButton OnClick="(() => updateNodeVisible = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
|
|
<MudButton OnClick="UpdateNode" Variant="Variant.Filled" Color="Color.Primary" Class="px-10 me-4" Disabled=@(MapIsActive)>Update</MudButton>
|
|
</DialogActions>
|
|
</MudDialog>
|
|
|
|
<MudDialog @bind-Visible="createElementVisible">
|
|
<TitleContent>
|
|
<MudText Typo="Typo.h6">
|
|
Create Element Node @ElementCreateModel.NodeId
|
|
</MudText>
|
|
</TitleContent>
|
|
<DialogContent>
|
|
<div class="d-flex flex-column" style="max-height: 700px; overflow-y: auto">
|
|
<MudTextField Class="my-2"
|
|
@bind-Value="ElementCreateModel.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 mb-2">
|
|
<MudNumericField Class="me-2" @bind-Value="ElementCreateModel.OffsetX" Label="Offset X" Step="0.1" Variant="Variant.Outlined" />
|
|
<MudNumericField Class="ms-2" @bind-Value="ElementCreateModel.OffsetY" Label="Offset Y" Step="0.1" Variant="Variant.Outlined" />
|
|
</div>
|
|
<MudSelect Class="mb-2" T="ElementModelDto" Variant="Variant.Outlined" Label="Type" Required="true" RequiredError="Type is required!"
|
|
@bind-Value="@ElementModelSelected">
|
|
@foreach (var model in ElementModels)
|
|
{
|
|
<MudSelectItem Value="@model">@model.Name</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</div>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<MudButton OnClick="(() => createElementVisible = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
|
|
<MudButton OnClick="CreateElement" Variant="Variant.Filled" Color="Color.Primary" Class="px-10" Disabled=@(MapIsActive)>Create</MudButton>
|
|
</DialogActions>
|
|
</MudDialog>
|
|
|
|
<MudDialog @bind-Visible="updateElementVisble">
|
|
<TitleContent>
|
|
<MudText Typo="Typo.h6">
|
|
Update Element @ElementUpdateModel.Name
|
|
</MudText>
|
|
</TitleContent>
|
|
<DialogContent>
|
|
<div class="d-flex flex-row" style="max-height: 700px; overflow-y: auto; width: 600px">
|
|
<div class="d-flex flex-column h-100 me-2" style="width: 47%">
|
|
<MudTextField T="string" Label="Name" @bind-value="ElementUpdateModel.Name" Variant="Variant.Outlined" ReadOnly="@(MapIsActive)"
|
|
Required="true" RequiredError="Name is required!"
|
|
Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
|
|
<MudNumericField T="double" Label="Offset X" @bind-value="ElementUpdateModel.OffsetX" Variant="Variant.Outlined" Step="0.1" ReadOnly="@(MapIsActive)" />
|
|
<MudNumericField T="double" Label="Offset Y" @bind-value="ElementUpdateModel.OffsetY" Variant="Variant.Outlined" Step="0.1" ReadOnly="@(MapIsActive)" />
|
|
@* <MudCheckBox Size="Size.Small" T="bool" LabelPlacement="Placement.Start" Label="IsOpen" @bind-value="ElementUpdateModel.IsOpen" Color="Color.Success"
|
|
UncheckedColor="Color.Default" ReadOnly /> *@
|
|
</div>
|
|
<div class="paper-property-container">
|
|
<div class="paper-property ms-2">
|
|
@foreach (var property in ElementProperties)
|
|
{
|
|
@if (property.Type == typeof(int).ToString())
|
|
{
|
|
if (int.TryParse(property.DefaultValue, out int value))
|
|
{
|
|
<MudNumericField T="int" Margin="Margin.Dense" Label="@property.Name" Value="value" Variant="Variant.Outlined" ReadOnly="@(MapIsActive)"
|
|
ValueChanged="((data) => DefaultIntValueChanged(property.Id, data))" />
|
|
}
|
|
}
|
|
else if (property.Type == typeof(double).ToString())
|
|
{
|
|
if (double.TryParse(property.DefaultValue, out double value))
|
|
{
|
|
<MudNumericField T="double" Margin="Margin.Dense" Label="@property.Name" Value="value" Variant="Variant.Outlined" Step="0.1" ReadOnly="@(MapIsActive)"
|
|
ValueChanged="((data) => DefaultDoubleValueChanged(property.Id, data))" />
|
|
}
|
|
}
|
|
else if (property.Type == typeof(bool).ToString())
|
|
{
|
|
if (bool.TryParse(property.DefaultValue, out bool value))
|
|
{
|
|
<MudCheckBox Class="m-0" Size="Size.Small" T="bool" LabelPlacement="Placement.Start" Label="@property.Name" Value="value" Color="Color.Success"
|
|
UncheckedColor="Color.Default" ReadOnly="@(MapIsActive)" ValueChanged="((data) => DefaultBooleanValueChanged(property.Id, data))" />
|
|
}
|
|
}
|
|
else if (property.Type == typeof(string).ToString())
|
|
{
|
|
<MudTextField T="string" Label="@property.Name" Value="@property.DefaultValue" Variant="Variant.Outlined" Margin="Margin.Dense" ReadOnly="@(MapIsActive)"
|
|
ValueChanged="((data) => property.DefaultValue = data)" />
|
|
}
|
|
}
|
|
</div>
|
|
<span class="paper-title">Properties</span>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<MudButton OnClick="Delete" Variant="Variant.Filled" Color="Color.Secondary" Class="px-10" Disabled=@(MapIsActive)>Delete</MudButton>
|
|
<MudButton OnClick="(() => updateElementVisble = false)" Class="px-10" Variant="Variant.Filled">Cancel</MudButton>
|
|
<MudButton OnClick="UpdateElement" Variant="Variant.Filled" Color="Color.Primary" Class="px-10" Disabled=@(MapIsActive)>Update</MudButton>
|
|
</DialogActions>
|
|
</MudDialog>
|
|
|
|
@code {
|
|
[Parameter, EditorRequired]
|
|
public MapNodeModel Models { get; set; } = null!;
|
|
|
|
[Parameter]
|
|
public MapElementModel Elements { get; set; } = [];
|
|
|
|
[CascadingParameter]
|
|
protected bool MapIsActive { get; set; }
|
|
|
|
[Parameter]
|
|
public bool ShowName { get; set; }
|
|
|
|
[Parameter]
|
|
public EditorState EditorState { get; set; }
|
|
|
|
private NodeUpdateModel UpdateModel = new();
|
|
private bool updateNodeVisible;
|
|
|
|
private List<ActionDto> Actions = [];
|
|
private ActionDto? ActionSelected = null;
|
|
|
|
public List<ElementModelDto> ElementModels { get; set; } = [];
|
|
private bool createElementVisible;
|
|
private ElementCreateModel ElementCreateModel = new();
|
|
private ElementModelDto? ElementModelSelected = null;
|
|
private NodeModel? NodeModelDoubleClick = null;
|
|
|
|
private bool updateElementVisble;
|
|
private ElementUpdateModel ElementUpdateModel = new();
|
|
private List<ElementProperty> ElementProperties = [];
|
|
private HttpClient Http = default!;
|
|
|
|
protected override void OnAfterRender(bool firstRender)
|
|
{
|
|
base.OnAfterRender(firstRender);
|
|
|
|
if (!firstRender) return;
|
|
|
|
Http = HttpClientFactory.CreateClient("MapManagerAPI");
|
|
Models.Changed += StateHasChanged;
|
|
Elements.Changed += StateHasChanged;
|
|
}
|
|
|
|
private void NodeClickChanged(NodeModel? node) => Models.SelectedNode = node;
|
|
|
|
private async Task LoadActionAsync(Guid mapId)
|
|
{
|
|
var result = await Http.GetFromJsonAsync<IEnumerable<ActionDto>>($"api/Actions/{mapId}");
|
|
|
|
if (result is not null && result.Any())
|
|
{
|
|
Actions.Clear();
|
|
Actions.AddRange(result);
|
|
}
|
|
if (Actions.Any()) ActionSelected = Actions.First();
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task LoadElementModels(Guid mapId)
|
|
{
|
|
ElementModels.Clear();
|
|
|
|
var elModels = await Http.GetFromJsonAsync<MessageResult<IEnumerable<ElementModelDto>>>($"api/ElementModels/map/{mapId}");
|
|
if (elModels is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
|
|
else if (!elModels.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {elModels.Message}", Severity.Error);
|
|
else if(elModels.Data is null || !elModels.Data.Any())
|
|
{
|
|
Snackbar.Add("Không có Element model nào trong bản đồ này", Severity.Warning);
|
|
ElementModelSelected = null;
|
|
}
|
|
else
|
|
{
|
|
ElementModels.AddRange(elModels.Data.OrderBy(el => el.Name));
|
|
if (ElementModels.Count > 0)
|
|
{
|
|
ElementModelSelected = ElementModelSelected is null ? ElementModels.First() : ElementModels.FirstOrDefault(em => em.Id == ElementModelSelected.Id) ?? ElementModels.First();
|
|
}
|
|
}
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void AddAction()
|
|
{
|
|
if (ActionSelected is null) return;
|
|
if (UpdateModel.Actions.Any(action => action == ActionSelected.Id))
|
|
{
|
|
Snackbar.Add("Action đã tồn tại", Severity.Warning);
|
|
return;
|
|
}
|
|
|
|
UpdateModel.Actions = [.. UpdateModel.Actions, ActionSelected.Id];
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void DeleteAction(Guid actionId)
|
|
{
|
|
UpdateModel.Actions = UpdateModel.Actions.Where(a => actionId != a).ToArray();
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task OnNodeDoubleClick(NodeModel model)
|
|
{
|
|
if (model == null || (EditorState != EditorState.NavigationEdit && EditorState != EditorState.View)) return;
|
|
|
|
NodeModelDoubleClick = model;
|
|
if (EditorState == EditorState.NavigationEdit)
|
|
{
|
|
await LoadActionAsync(model.MapId);
|
|
|
|
UpdateModel.Id = model.Id;
|
|
UpdateModel.Name = model.Name;
|
|
UpdateModel.X = model.X;
|
|
UpdateModel.Y = model.Y;
|
|
UpdateModel.Theta = model.Theta;
|
|
UpdateModel.AllowedDeviationXy = model.AllowedDeviationXy;
|
|
UpdateModel.AllowedDeviationTheta = model.AllowedDeviationTheta;
|
|
UpdateModel.Actions = [];
|
|
var actions = JsonSerializer.Deserialize<Guid[]>(model.Actions ?? "");
|
|
if (actions is not null && actions.Length > 0)
|
|
{
|
|
UpdateModel.Actions = [.. actions];
|
|
}
|
|
|
|
updateNodeVisible = true;
|
|
}
|
|
else if (EditorState == EditorState.View)
|
|
{
|
|
await LoadElementModels(model.MapId);
|
|
|
|
ElementCreateModel.Name = "";
|
|
ElementCreateModel.MapId = model.MapId;
|
|
ElementCreateModel.NodeId = model.Id;
|
|
|
|
createElementVisible = true;
|
|
}
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task UpdateNode()
|
|
{
|
|
var selectedModel = Models.FirstOrDefault(e => e.Id == UpdateModel.Id);
|
|
|
|
if (selectedModel == null)
|
|
{
|
|
updateNodeVisible = false;
|
|
StateHasChanged();
|
|
return;
|
|
}
|
|
|
|
var result = await (await Http.PutAsJsonAsync($"api/Nodes", UpdateModel)).Content.ReadFromJsonAsync<MessageResult>();
|
|
if (result is null)
|
|
{
|
|
Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
|
|
return;
|
|
}
|
|
else if (!result.IsSuccess)
|
|
{
|
|
Snackbar.Add(result.Message, Severity.Error);
|
|
return;
|
|
}
|
|
|
|
selectedModel.UpdateData(UpdateModel);
|
|
updateNodeVisible = false;
|
|
Snackbar.Add("Cập nhật thành công", Severity.Success);
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task CreateElement()
|
|
{
|
|
try
|
|
{
|
|
if (NodeModelDoubleClick is null) return;
|
|
if (ElementModelSelected is null)
|
|
{
|
|
Snackbar.Add("Hãy chọn Element model!", Severity.Warning);
|
|
return;
|
|
}
|
|
if (string.IsNullOrEmpty(ElementCreateModel.Name))
|
|
{
|
|
Snackbar.Add("Name không được để trống.", Severity.Warning);
|
|
return;
|
|
}
|
|
|
|
var nameInvalid = MapEditorHelper.NameChecking(ElementCreateModel.Name);
|
|
if (!nameInvalid.IsSuccess)
|
|
{
|
|
Snackbar.Add(nameInvalid.returnStr, Severity.Warning);
|
|
return;
|
|
}
|
|
|
|
ElementCreateModel.ModelId = ElementModelSelected.Id;
|
|
|
|
var create = await (await Http.PostAsJsonAsync("api/Elements", ElementCreateModel)).Content.ReadFromJsonAsync<MessageResult<ElementDto>>();
|
|
if (create is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
|
|
else if (!create.IsSuccess) Snackbar.Add($"Có lỗi xảy ra: {create.Message}", Severity.Error);
|
|
else if (create.Data is null) Snackbar.Add("Tạo Element không thành công", Severity.Error);
|
|
else
|
|
{
|
|
|
|
Elements.Add(new(create.Data, NodeModelDoubleClick));
|
|
createElementVisible = false;
|
|
Snackbar.Add("Tạo Element thành công!", Severity.Success);
|
|
}
|
|
StateHasChanged();
|
|
}
|
|
catch (AccessTokenNotAvailableException ex)
|
|
{
|
|
ex.Redirect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void ElementDoubleClick(ElementModel 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;
|
|
|
|
if (model is not null && !string.IsNullOrEmpty(model.Content))
|
|
{
|
|
var properties = JsonSerializer.Deserialize<List<ElementProperty>>(model.Content, JsonOptionExtends.Read);
|
|
if (properties is not null) ElementProperties = [.. properties];
|
|
}
|
|
|
|
updateElementVisble = true;
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task Delete()
|
|
{
|
|
var parameters = new DialogParameters<ConfirmDialog>
|
|
{
|
|
{ x => x.Content, $"Bạn có chắc chắn muốn xóa element {ElementUpdateModel.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/{ElementUpdateModel.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 == ElementUpdateModel.Id);
|
|
if (elementData is not null)
|
|
{
|
|
Elements.Remove(elementData);
|
|
Snackbar.Add($"Xóa element thành công.", Severity.Success);
|
|
}
|
|
else Snackbar.Add($"Element không tồn tại.", Severity.Warning);
|
|
}
|
|
|
|
updateElementVisble = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private void DefaultIntValueChanged(Guid id, int value)
|
|
{
|
|
var property = ElementProperties.FirstOrDefault(p => p.Id == id);
|
|
if (property is null) return;
|
|
property.DefaultValue = value.ToString();
|
|
}
|
|
|
|
private void DefaultDoubleValueChanged(Guid id, double value)
|
|
{
|
|
var property = ElementProperties.FirstOrDefault(p => p.Id == id);
|
|
if (property is null) return;
|
|
property.DefaultValue = value.ToString();
|
|
}
|
|
|
|
private void DefaultBooleanValueChanged(Guid id, bool value)
|
|
{
|
|
var property = ElementProperties.FirstOrDefault(p => p.Id == id);
|
|
if (property is null) return;
|
|
property.DefaultValue = value.ToString();
|
|
}
|
|
|
|
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 = JsonSerializer.Serialize(ElementProperties, JsonOptionExtends.Write),
|
|
})).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.UpdateElement(result.Data);
|
|
Snackbar.Add("Cập nhật thành công", Severity.Success);
|
|
}
|
|
|
|
updateElementVisble = false;
|
|
StateHasChanged();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Models.Changed -= StateHasChanged;
|
|
Elements.Changed -= StateHasChanged;
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|