Skip to content

Commit 59d3839

Browse files
committed
role menu select
1 parent 6d8e710 commit 59d3839

File tree

2 files changed

+172
-60
lines changed

2 files changed

+172
-60
lines changed

src/BlazorAdmin/BlazorAdmin.Modules/BlazorAdmin.Rbac/Pages/Role/Dialogs/RoleMenuDialog.razor

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,49 @@
11
<MudDialog>
22
<DialogContent>
3-
<div style="width:300px;">
4-
<MudTreeView T="MenuTreeItem" Items="@MenuTreeItemSet" Hover="true">
5-
<ItemTemplate>
6-
<MudTreeViewItem @bind-Expanded="@context.Expanded" Items="@context.Children" Value="@context.Value">
7-
<Content>
8-
<MudTreeViewItemToggleButton @bind-Expanded="@context.Expanded"
9-
Visible="context.HasChildren" />
10-
<MudCheckBox Value="@context.Value.IsChecked" Dense="true" T="bool"
11-
ValueChanged="@((v) => CheckedChanged(v, context))">
12-
</MudCheckBox>
13-
<MudText Typo="Typo.subtitle2" Style="user-select:none;">@context.Value.MenuName</MudText>
14-
<MudSpacer />
15-
</Content>
16-
</MudTreeViewItem>
17-
</ItemTemplate>
18-
</MudTreeView>
3+
<div style="min-width:750px;">
4+
<MudTable Items="@MenuTableRows" Hover="true" Dense="true">
5+
<HeaderContent>
6+
<MudTh Style="width: 150px;">菜单</MudTh>
7+
<MudTh Style="">按钮</MudTh>
8+
</HeaderContent>
9+
<RowTemplate>
10+
<MudTd>
11+
<div class="d-flex align-center">
12+
@{
13+
// 根据层级添加缩进
14+
var paddingLeft = context.Menu.Level * 20;
15+
}
16+
<div style="padding-left: @(paddingLeft)px" class="d-flex align-center">
17+
<MudCheckBox Value="@context.Menu.IsChecked" Dense="true" T="bool"
18+
ValueChanged="@((v) => MenuCheckedChanged(v, context.Menu))">
19+
</MudCheckBox>
20+
@if (!string.IsNullOrEmpty(context.Menu.MenuIcon))
21+
{
22+
<MudIcon Icon="@context.Menu.MenuIcon" Class="mr-2" />
23+
}
24+
<MudText Typo="Typo.subtitle2">@context.Menu.MenuName</MudText>
25+
</div>
26+
</div>
27+
</MudTd>
28+
<MudTd>
29+
<div class="d-flex flex-wrap gap-2">
30+
@foreach (var button in context.Buttons)
31+
{
32+
<div class="d-flex align-center mr-4">
33+
<MudCheckBox Value="@button.IsChecked" Dense="true" T="bool"
34+
ValueChanged="@((v) => ButtonCheckedChanged(v, button))">
35+
</MudCheckBox>
36+
<MudText Typo="Typo.body2">@button.MenuName</MudText>
37+
</div>
38+
}
39+
</div>
40+
</MudTd>
41+
</RowTemplate>
42+
</MudTable>
1943

20-
<div class=" d-flex">
44+
<div class="d-flex mt-4">
2145
<MudSpacer />
22-
<MudButton Class="mt-2" StartIcon="@Icons.Material.Filled.Upload" FullWidth="false" Variant="Variant.Text" Color="Color.Primary"
46+
<MudButton StartIcon="@Icons.Material.Filled.Upload" Variant="Variant.Text" Color="Color.Primary"
2347
OnClick="SubmitRoleMenu">
2448
@Loc["RolePage_SubmitText"]
2549
</MudButton>

src/BlazorAdmin/BlazorAdmin.Modules/BlazorAdmin.Rbac/Pages/Role/Dialogs/RoleMenuDialog.razor.cs

Lines changed: 130 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public partial class RoleMenuDialog
1313

1414
private List<Data.Entities.Rbac.Menu> MenuList = new();
1515

16-
private List<TreeItemData<MenuTreeItem>> MenuTreeItemSet = new();
16+
private List<MenuTableRow> MenuTableRows = new();
1717

1818
private List<int> CheckedMenuIdList = new();
1919

@@ -24,23 +24,52 @@ protected override async Task OnInitializedAsync()
2424
using var context = await _dbFactory.CreateDbContextAsync();
2525
CheckedMenuIdList = context.RoleMenus.Where(rm => rm.RoleId == RoleId).Select(rm => rm.MenuId).ToList();
2626
MenuList = context.Menus.ToList();
27-
MenuTreeItemSet = AppendMenuItems(null, MenuList);
27+
28+
// 处理菜单层级结构
29+
BuildMenuTableRows();
2830
}
2931

30-
private List<TreeItemData<MenuTreeItem>> AppendMenuItems(int? parentId, List<Data.Entities.Rbac.Menu> menus)
32+
private void BuildMenuTableRows()
3133
{
32-
return menus.Where(m => m.ParentId == parentId).OrderBy(m => m.Order)
33-
.Select(m => new TreeItemData<MenuTreeItem>
34+
// 构建顶级菜单
35+
BuildMenuLevelRows(null, 0);
36+
}
37+
38+
private void BuildMenuLevelRows(int? parentId, int level)
39+
{
40+
// 获取当前层级的菜单项
41+
var menuItems = MenuList.Where(m => m.Type == 1 && m.ParentId == parentId)
42+
.OrderBy(m => m.Order)
43+
.ToList();
44+
45+
foreach (var menu in menuItems)
46+
{
47+
// 创建当前菜单行
48+
var menuRow = new MenuTableRow
3449
{
35-
Value = new MenuTreeItem
50+
Menu = new MenuItemViewModel
3651
{
37-
Id = m.Id,
38-
IsChecked = CheckedMenuIdList.Contains(m.Id),
39-
MenuIcon = m.Icon,
40-
MenuName = m.Name,
52+
Id = menu.Id,
53+
IsChecked = CheckedMenuIdList.Contains(menu.Id),
54+
MenuIcon = menu.Icon,
55+
MenuName = menu.Name,
56+
Level = level
4157
},
42-
Children = AppendMenuItems(m.Id, menus)
43-
}).ToList();
58+
Buttons = MenuList.Where(m => m.Type == 2 && m.ParentId == menu.Id)
59+
.OrderBy(m => m.Order)
60+
.Select(button => new MenuItemViewModel
61+
{
62+
Id = button.Id,
63+
IsChecked = CheckedMenuIdList.Contains(button.Id),
64+
MenuName = button.Name
65+
}).ToList()
66+
};
67+
68+
MenuTableRows.Add(menuRow);
69+
70+
// 递归处理子菜单
71+
BuildMenuLevelRows(menu.Id, level + 1);
72+
}
4473
}
4574

4675
#endregion
@@ -51,7 +80,8 @@ private async Task SubmitRoleMenu()
5180
{
5281
using var context = await _dbFactory.CreateDbContextAsync();
5382

54-
var idList = GetSelectedValues(MenuTreeItemSet);
83+
// 获取所有选中的菜单和按钮ID
84+
var idList = GetSelectedValues();
5585

5686
var roleMenuList = context.RoleMenus.Where(rm => rm.RoleId == RoleId).ToList();
5787

@@ -71,66 +101,114 @@ private async Task SubmitRoleMenu()
71101
MudDialog?.Close(DialogResult.Ok(true));
72102
}
73103

74-
private List<int> GetSelectedValues(List<TreeItemData<MenuTreeItem>> set)
104+
private List<int> GetSelectedValues()
75105
{
76-
return set.Where(i => i.Value.IsChecked).Select(i => i.Value.Id)
77-
.Concat(set.SelectMany(i => GetSelectedValues(i.Children)))
78-
.ToList();
106+
var result = new List<int>();
107+
108+
// 添加选中的菜单ID
109+
result.AddRange(MenuTableRows.Where(row => row.Menu.IsChecked).Select(row => row.Menu.Id));
110+
111+
// 添加选中的按钮ID
112+
foreach (var row in MenuTableRows)
113+
{
114+
result.AddRange(row.Buttons.Where(btn => btn.IsChecked).Select(btn => btn.Id));
115+
}
116+
117+
return result;
79118
}
80119

81120
#endregion
82121

83-
84122
#region checkbox checked change event
85123

86-
private void CheckedChanged(bool value, TreeItemData<MenuTreeItem> item)
124+
private void MenuCheckedChanged(bool value, MenuItemViewModel item)
87125
{
88-
LoopCheckedChange(value, item);
89-
if (value)
126+
item.IsChecked = value;
127+
128+
// 如果取消选中菜单,同时取消选中其所有按钮和子菜单
129+
if (!value)
130+
{
131+
// 取消所有按钮
132+
var row = MenuTableRows.FirstOrDefault(r => r.Menu.Id == item.Id);
133+
if (row != null)
134+
{
135+
foreach (var button in row.Buttons)
136+
{
137+
button.IsChecked = false;
138+
}
139+
}
140+
141+
// 递归取消所有子菜单
142+
UncheckChildMenus(item.Id);
143+
}
144+
else
90145
{
91-
LoopParentChecked(item);
146+
// 如果选中菜单,确保其所有父菜单也被选中
147+
CheckParentMenus(item.Id);
92148
}
93149
}
94150

95-
private void LoopCheckedChange(bool value, TreeItemData<MenuTreeItem> item)
151+
private void UncheckChildMenus(int parentId)
96152
{
97-
item.Value.IsChecked = value;
98-
foreach (var i in item.Children)
153+
// 查找所有子菜单行
154+
var childRows = MenuTableRows.Where(r => MenuList.Any(m => m.Id == r.Menu.Id && m.ParentId == parentId)).ToList();
155+
156+
foreach (var childRow in childRows)
99157
{
100-
LoopCheckedChange(value, i);
158+
childRow.Menu.IsChecked = false;
159+
160+
// 取消所有按钮
161+
foreach (var button in childRow.Buttons)
162+
{
163+
button.IsChecked = false;
164+
}
165+
166+
// 递归处理子菜单的子菜单
167+
UncheckChildMenus(childRow.Menu.Id);
101168
}
102169
}
103170

104-
private void LoopParentChecked(TreeItemData<MenuTreeItem> item)
171+
private void CheckParentMenus(int menuId)
105172
{
106-
item.Value.IsChecked = true;
107-
var parent = FindParentItem(item.Value, MenuTreeItemSet);
108-
if (parent != null)
173+
// 找到当前菜单
174+
var menu = MenuList.FirstOrDefault(m => m.Id == menuId);
175+
if (menu?.ParentId != null)
109176
{
110-
LoopParentChecked(parent);
177+
// 找到父菜单行
178+
var parentRow = MenuTableRows.FirstOrDefault(r => r.Menu.Id == menu.ParentId);
179+
if (parentRow != null && !parentRow.Menu.IsChecked)
180+
{
181+
parentRow.Menu.IsChecked = true;
182+
183+
// 递归处理父菜单的父菜单
184+
CheckParentMenus(parentRow.Menu.Id);
185+
}
111186
}
112187
}
113188

114-
private TreeItemData<MenuTreeItem>? FindParentItem(MenuTreeItem item, List<TreeItemData<MenuTreeItem>> itemList)
189+
private void ButtonCheckedChanged(bool value, MenuItemViewModel item)
115190
{
116-
var parent = itemList.FirstOrDefault(i => i.Children.Any(c => c.Value.Id == item.Id));
117-
if (parent == null)
191+
item.IsChecked = value;
192+
193+
// 如果选中按钮,确保其所属菜单也被选中
194+
if (value)
118195
{
119-
foreach (var i in itemList)
196+
var row = MenuTableRows.FirstOrDefault(r => r.Buttons.Any(b => b.Id == item.Id));
197+
if (row != null)
120198
{
121-
parent = FindParentItem(item, i.Children);
122-
if (parent != null)
123-
{
124-
return parent;
125-
}
199+
row.Menu.IsChecked = true;
200+
201+
// 确保所有父菜单也被选中
202+
CheckParentMenus(row.Menu.Id);
126203
}
127204
}
128-
return parent;
129205
}
130206

131207
#endregion
132208

133-
private class MenuTreeItem
209+
#region Models
210+
211+
private class MenuItemViewModel
134212
{
135213
public int Id { get; set; }
136214

@@ -139,7 +217,17 @@ private class MenuTreeItem
139217
public string? MenuName { get; set; }
140218

141219
public bool IsChecked { get; set; }
220+
221+
public int Level { get; set; } = 0;
222+
}
142223

224+
private class MenuTableRow
225+
{
226+
public MenuItemViewModel Menu { get; set; } = new();
227+
228+
public List<MenuItemViewModel> Buttons { get; set; } = new();
143229
}
230+
231+
#endregion
144232
}
145233
}

0 commit comments

Comments
 (0)