feat: 完善菜单维护父级菜单功能

This commit is contained in:
Argo-Tianyi 2022-02-28 19:37:27 +08:00 committed by zhangpeihang
parent 1f2a169cc7
commit 32d74ea81a
12 changed files with 250 additions and 33 deletions

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup> <ItemGroup>
<PackageReference Include="BootstrapBlazor" Version="6.2.9-beta07" /> <PackageReference Include="BootstrapBlazor" Version="6.3.4-beta03" />
<PackageReference Include="Longbow.Security.Cryptography" Version="5.2.0" /> <PackageReference Include="Longbow.Security.Cryptography" Version="5.2.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.1" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.1" />
<PackageReference Include="PetaPoco.Extensions" Version="6.0.0" /> <PackageReference Include="PetaPoco.Extensions" Version="6.0.0" />

View File

@ -0,0 +1,29 @@
<div class="row g-3 form-inline">
<div class="col-12 col-sm-6 col-md-6">
<BootstrapInput @bind-Value="Value.Name" DisplayText="菜单名称" ShowLabel="true"></BootstrapInput>
</div>
<div class="col-12 col-sm-6 col-md-6">
<MenuTree @bind-Value="Value.ParentId" Lookup="@ParementMenus" DisplayText="父级菜单"></MenuTree>
</div>
<div class="col-12 col-sm-6 col-md-6">
<BootstrapInput @bind-Value="Value.Order"></BootstrapInput>
</div>
<div class="col-12 col-sm-6 col-md-6">
<BootstrapInput @bind-Value="Value.Icon"></BootstrapInput>
</div>
<div class="col-12">
<BootstrapInput @bind-Value="Value.Url"></BootstrapInput>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select @bind-Value="Value.Category"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select Items="@Targets" @bind-Value="Value.Target"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select @bind-Value="Value.IsResource"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select Items="@Apps" @bind-Value="Value.Application"></Select>
</div>
</div>

View File

@ -0,0 +1,42 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using BootstrapAdmin.DataAccess.Models;
namespace BootstrapAdmin.Web.Components;
public partial class MenuEditor
{
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public Navigation? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<SelectedItem>? ParementMenus { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<SelectedItem>? Targets { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<SelectedItem>? Apps { get; set; }
}

View File

@ -0,0 +1,8 @@
<div class="tree-dialog">
<label class="form-label">@DisplayText</label>
<BootstrapInputGroup>
<Display Value="Value" Lookup="Lookup" ShowLabel="false"></Display>
<Button Icon="fa fa-times" Color="Color.Secondary" OnClick="OnClearText"></Button>
<Button Icon="fa fa-bars" OnClick="OnSelectMenu"></Button>
</BootstrapInputGroup>
</div>

View File

@ -0,0 +1,87 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using BootstrapAdmin.Web.Core;
using BootstrapAdmin.Web.Services;
using Microsoft.AspNetCore.Components.Web;
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class MenuTree
{
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public List<SelectedItem>? Lookup { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public string? DisplayText { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public string? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public EventCallback<string> ValueChanged { get; set; }
[Inject]
[NotNull]
private DialogService? DialogService { get; set; }
private DialogOption? Option { get; set; }
private async Task OnSelectMenu()
{
Option = new DialogOption()
{
IsScrolling = true,
Title = "选择菜单",
BodyTemplate = BootstrapDynamicComponent.CreateComponent<ParentMenuTree>(new Dictionary<string, object?>
{
[nameof(ParentMenuTree.Value)] = Value,
[nameof(ParentMenuTree.ValueChanged)] = EventCallback.Factory.Create<string>(this, v => OnValueChanged(v))
}).Render(),
FooterTemplate = BootstrapDynamicComponent.CreateComponent<Button>(new Dictionary<string, object?>
{
[nameof(Button.Color)] = Color.Primary,
[nameof(Button.Text)] = "确认",
[nameof(Button.OnClick)] = EventCallback.Factory.Create<MouseEventArgs>(this, () =>
{
Option?.Dialog.Close();
}),
}).Render()
};
await DialogService.Show(Option);
}
private Task OnClearText() => OnValueChanged("0");
private async Task OnValueChanged(string v)
{
if (Value != v)
{
Value = v;
if (ValueChanged.HasDelegate)
{
await ValueChanged.InvokeAsync(Value);
}
}
}
}

View File

@ -0,0 +1,7 @@
.tree-dialog {
display: flex;
}
.tree-dialog ::deep .input-group {
flex-wrap: nowrap !important
}

View File

@ -5,4 +5,4 @@
@code { @code {
RenderFragment<Navigation> RenderTreeItem => item => RenderFragment<Navigation> RenderTreeItem => item =>
@<div class="d-flex flex-fill"><span class="flex-fill">@item.Name</span><span class="mx-3">@item.Order</span><span class="app-type">@item.IsResource.ToDescriptionString()</span><span class="app-text">@GetApp(item.Application)</span></div>; @<div class="d-flex flex-fill"><span class="flex-fill">@item.Name</span><span class="mx-3">@item.Order</span><span class="app-type">@item.IsResource.ToDescriptionString()</span><span class="app-text">@GetApp(item.Application)</span></div>;
} }

View File

@ -0,0 +1,6 @@
<Tree Items="InternalItems" ShowRadio="true" ShowIcon="true" OnTreeItemChecked="@OnTreeItemChecked" />
@code {
RenderFragment<Navigation> RenderTreeItem => item =>
@<div class="d-flex flex-fill"><span class="flex-fill">@item.Name</span><span class="mx-3">@item.Order</span><span class="app-type">@item.IsResource.ToDescriptionString()</span><span class="app-text">@GetApp(item.Application)</span></div>;
}

View File

@ -0,0 +1,66 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using BootstrapAdmin.Web.Core;
using BootstrapAdmin.Web.Extensions;
using BootstrapAdmin.Web.Services;
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class ParentMenuTree
{
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public string? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
[NotNull]
private List<TreeItem>? InternalItems { get; set; }
[Inject]
[NotNull]
private INavigation? NavigationService { get; set; }
[Inject]
[NotNull]
private IDict? DictService { get; set; }
[Inject]
[NotNull]
private BootstrapAppContext? Context { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
var items = NavigationService.GetAllMenus(Context.UserName);
InternalItems = items.ToTreeItemList(new List<string> { Value }, RenderTreeItem);
}
private async Task OnTreeItemChecked(List<TreeItem> items)
{
Value = items.First().Key?.ToString();
if (ValueChanged.HasDelegate)
{
await ValueChanged.InvokeAsync(Value);
}
}
private string GetApp(string? app) => DictService.GetApps().FirstOrDefault(i => i.Key == app).Value ?? "未设置";
}

View File

@ -23,35 +23,7 @@
<TableColumn @bind-Field="@context.Application" Filterable="true" Lookup="Apps"></TableColumn> <TableColumn @bind-Field="@context.Application" Filterable="true" Lookup="Apps"></TableColumn>
</TableColumns> </TableColumns>
<EditTemplate Context="v"> <EditTemplate Context="v">
<div class="row g-3 form-inline"> <MenuEditor Value="v" ParementMenus="ParementMenus" Targets="Targets" Apps="Apps" />
<div class="col-12 col-sm-6 col-md-6">
<BootstrapInput @bind-Value="v.Name" DisplayText="菜单名称" ShowLabel="true"></BootstrapInput>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select @bind-Value="v.ParentId" Items="@ParementMenus" IsDisabled="@(v.ParentId == "0")" DisplayText="父级菜单" ShowLabel="true"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<BootstrapInput @bind-Value="v.Order"></BootstrapInput>
</div>
<div class="col-12 col-sm-6 col-md-6">
<BootstrapInput @bind-Value="v.Icon"></BootstrapInput>
</div>
<div class="col-12">
<BootstrapInput @bind-Value="v.Url"></BootstrapInput>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select @bind-Value="v.Category"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select Items="@Targets" @bind-Value="v.Target"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select @bind-Value="v.IsResource"></Select>
</div>
<div class="col-12 col-sm-6 col-md-6">
<Select Items="@Apps" @bind-Value="v.Application"></Select>
</div>
</div>
</EditTemplate> </EditTemplate>
<RowButtonTemplate> <RowButtonTemplate>
<TableCellButton Size="Size.ExtraSmall" IsShow="@AuthorizeButton("assignRole")" Color="Color.Info" Icon="fa fa-sitemap" Text="分配角色" OnClick="() => OnAssignmentRoles(context)" /> <TableCellButton Size="Size.ExtraSmall" IsShow="@AuthorizeButton("assignRole")" Color="Color.Info" Icon="fa fa-sitemap" Text="分配角色" OnClick="() => OnAssignmentRoles(context)" />

View File

@ -44,5 +44,5 @@ class AdminTaskService : BackgroundService
// 真实任务负责周期性设置健康检查结果开关为开启 // 真实任务负责周期性设置健康检查结果开关为开启
TaskServicesManager.GetOrAdd("健康检查", token => Task.FromResult(DictService.SaveHealthCheck()), TriggerBuilder.Build(Cron.Minutely(10))); TaskServicesManager.GetOrAdd("健康检查", token => Task.FromResult(DictService.SaveHealthCheck()), TriggerBuilder.Build(Cron.Minutely(10)));
}); }, stoppingToken);
} }

View File

@ -13,4 +13,4 @@
@using BootstrapAdmin.Web.Components @using BootstrapAdmin.Web.Components
@using BootstrapAdmin.Web.Models @using BootstrapAdmin.Web.Models
@using BootstrapAdmin.Web.Shared @using BootstrapAdmin.Web.Shared