数据库表格的设计

菜单的实体类
定义菜单数据模型: 首先,你需要定义一个菜单数据模型来表示菜单项。该模型应当包含菜单的ID、名称、URL以及子菜单列表等属性。
查询数据库: 在数据库中存储菜单项的数据,并编写适当的查询方法从数据库中检索菜单项数据。你可以使用Entity Framework Core或其他适用的ORM工具来简化数据库查询工作。
构建递归菜单: 在你获取菜单项数据后,通过递归方法来构建递归菜单。递归方法将遍历菜单项列表,并根据菜单项的父子关系递归地构建菜单层次结构。最终,该方法将返回一个拥有正确层次结构的菜单项列表。
public class MenuInfo : BaseEntity
{
//[Column(TypeName = "varchar(36)")]
//public string Id { get; set; } /*主键Id */
[Column(TypeName = "varchar(36)")]
public string Title { get; set; } /*标题 */
[Column(TypeName = "varchar(36)")]
public string? Description { get; set; } /*描述 */
public int Level { get; set; }/*等级 */
public int Sort { get; set; } /*排序 */
[Column(TypeName = "varchar(100)")]
public string? Href { get; set; } /*访问地址*/
[Column(TypeName = "varchar(36)")]
public string? ParentId { get; set; } /*父菜单id*/
[Column(TypeName = "varchar(36)")]
public string Icon { get; set; } /*图标样式*/
[Column(TypeName = "varchar(36)")]
public string Target { get; set; } /*目标 */
public DateTime CreateTime { get; set; } /*添加时间*/
public bool IsDelete { get; set; }/*是否删除*/
public DateTime? DeleteTime { get; set; }/*删除时间*/
}

我是通过继承BaseEntity来使用里面的id,这样每次创建实体类就不需要手动加id字段了。
其中可以通过ParentId来实现多级菜单,后面会进行数据库展示。
业务逻辑层Bll
从数据源获取菜单数据: 接下来,你需要从数据源(如数据库)中获取菜单数据并构建菜单项。可以使用Entity Framework Core或其他数据访问技术来查询菜单数据。
在递归方法中,你可以首先查询具有特定父菜单ID的菜单项,并对每个菜单项进行递归调用,处理其子菜单。
/// <summary>
/// 获取菜单
/// </summary>
/// <returns></returns>
public List<HomeIndexMenu> HomeIndexMenuInfo(string userId)
{
UserInfo userInfo = _userInfoDAL.GetInfos().FirstOrDefault(u => u.Id == userId);
if (userInfo == null)
{
return new List<HomeIndexMenu>();
}
List<HomeIndexMenu> GetMenu;
//是否是管理员
if (userInfo.IsAdmin)
{
GetMenu = _MenuInfoDAL.GetInfos().Select(m => new HomeIndexMenu()
{
Title = m.Title,
Target = m.Target,
Href = m.Href,
Icon = m.Icon,
Level = m.Level,
ParentId = m.ParentId,
Id = m.Id
}).ToList();
}
else
{
List<string> GetMenuId = (from ur in _role_UserInfoDAL.GetInfos().Where(u => u.UserId == userId)
join
rm in _R_Role_MenuDAL.GetInfos()
on ur.RoleId equals rm.RoleId
select rm.MenuId).ToList();
//先查出所有的菜单
GetMenu = _MenuInfoDAL.GetInfos().Where(m => GetMenuId.Contains(m.Id)).Select(m => new HomeIndexMenu()
{
Title = m.Title,
Target = m.Target,
Href = m.Href,
Icon = m.Icon,
Level = m.Level,
ParentId = m.ParentId,
Id = m.Id
}).ToList();
}
//找出一级菜单
var GetParentMenu = GetMenu.Where(m => m.Level == 1).ToList();
//递归找多级菜单
FindChildMenu(GetParentMenu, GetMenu);
return GetParentMenu;
}
/// <summary>
/// 寻找子菜单
/// </summary>
/// <param name="GetParentMenu"></param>
/// <param name="GetMenu"></param>
public void FindChildMenu(List<HomeIndexMenu> GetParentMenu, List<HomeIndexMenu> GetMenu)
{
foreach (var item in GetParentMenu)
{
var GetChildMenu = GetMenu.Where(m => m.ParentId == item.Id && m.Level == item.Level + 1).ToList();
if (GetChildMenu != null && GetChildMenu.Count() > 0)
{
item.Child = GetChildMenu;
FindChildMenu(GetChildMenu, GetMenu);
}
}
}
1. **模型设计:** 首先,你需要设计一个菜单模型来表示菜单项的属性。菜单模型应至少包含菜单ID、菜单名称、菜单URL和父菜单ID等字段。父菜单ID用于建立菜单之间的父子关系。
2. **数据查询:** 使用数据库访问工具(如Entity Framework Core)或手动编写SQL查询,从数据库中检索菜单数据。查询可以基于父菜单ID进行过滤,以获取特定层级的菜单项。
3. **递归处理:** 在ASP.NET Core中,你可以使用递归方法来构建递归菜单。递归方法接收父菜单ID作为参数,并在每个递归调用中查询并处理子菜单。
数据访问层DAL

public class BaseDAL<T> : IBaseDAL<T> where T : BaseEntity
{
UserInfoDbContext _dbContext;
public BaseDAL(UserInfoDbContext dbContext)
{
_dbContext = dbContext;
}
/// <summary>
/// 添加的数据层
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name=""></param>
/// <returns></returns>
public bool Add(T info)
{
_dbContext.Set<T>().Add(info);
int i = _dbContext.SaveChanges();
if (i > 0)
{
return true;
}
return false;
}
/// <summary>
/// 删除方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool DeleteInfo(T Entity)
{
_dbContext.Set<T>().Remove(Entity);
int i = _dbContext.SaveChanges();
if (i > 0)
{
return true;
}
return false;
}
//重载删除方法 通过继承类来调用id传值
public bool DeleteInfo(string id)
{
var entity = _dbContext.Set<T>().FirstOrDefault(u => u.Id == id);
if (entity != null)
{
_dbContext.Set<T>().Remove(entity);
int i = _dbContext.SaveChanges();
if (i > 0)
{
return true;
}
return false;
}
else
{
return false;
}
}
/// <summary>
/// 批量删除方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Entity"></param>
/// <returns></returns>
public bool DeleteInfoPL(List<T> Entity)
{
_dbContext.Set<T>().RemoveRange(Entity);
int i = _dbContext.SaveChanges();
if (i > 0)
{
return true;
}
return false;
}
/// <summary>
/// 获取数据
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public DbSet<T> GetInfos()
{
return _dbContext.Set<T>();
}
/// <summary>
/// 更改数据
/// </summary>
/// <param name="departmentInfo"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool UpdateInfo(T info)
{
_dbContext.Set<T>().Update(info);
int i = _dbContext.SaveChanges();
if (i > 0)
{
return true;
}
return false;
}
}
通过写了一个通过的BaseDAL里面封装了增删改查的方法
Controller层
/// <summary>
/// 获取全部菜单数据
/// </summary>
/// <param name="page"></param>
/// <param name="limit"></param>
/// <param name="Title"></param>
/// <param name="Description"></param>
/// <returns></returns>
public MyJsonRes GetAllMenutInfo(int page, int limit, string Title, string Description)
{
int count;
//参数判断
if (page <= 0)
{
return MyJsonRes.MyRes(msg: "页码有误");
}
if (limit <= 0)
{
return MyJsonRes.MyRes(msg: "页数有误");
}
object list = _menuInfoBll.GetAllMenutInfo(page, limit, Title, Description, out count);
return MyJsonRes.MyRes(0, "查询成功", count, data: list);
}
数据库数据

我们可以通过观察系统管理2的父级菜单系统管理

返回给前端的JSON数据
{
"code": 0,
"msg": "查询成功",
"data": [
{
"id": "37ef38dc-5f7c-4e58-83f0-275362dae316",
"title": "系统管理4",
"description": "4100",
"level": 4,
"parentId": null,
"href": null,
"target": "_self",
"createTime": "2023-11-09T10:07:09.2929972"
},
{
"id": "3245827f-2158-48e9-8de1-0c4faf36d878",
"title": "系统管理3",
"description": "我是系统管理3",
"level": 3,
"parentId": "6583f127-32fe-44a1-9c06-4903aa93c1f9",
"href": null,
"target": "_self",
"createTime": "2023-11-09T10:02:45.0575461"
},
{
"id": "6583f127-32fe-44a1-9c06-4903aa93c1f9",
"title": "系统管理2",
"description": "我是系统管理2",
"level": 2,
"parentId": "aa3a6c67-f41a-4dc8-a39f-09811f835e1e",
"href": null,
"target": "_self",
"createTime": "2023-11-09T10:02:24.6155799"
},
{
"id": "aa3a6c67-f41a-4dc8-a39f-09811f835e1e",
"title": "系统管理",
"description": "我是系统管理",
"level": 1,
"parentId": null,
"href": null,
"target": "_self",
"createTime": "2023-11-09T10:01:51.8141847"
},
{
"id": "7a637559-32d6-4271-ab7c-437491dbc082",
"title": "工作模板",
"description": "我是工作模板",
"level": 1,
"parentId": null,
"href": "/WorkFlow_ModelInfo/WorkFlow_ModelInfoView",
"target": "_self",
"createTime": "2023-10-19T18:09:03.4366009"
},
{
"id": "40ee34b4-07d1-4ccd-ac5d-2abdfe93d16f",
"title": "耗材类别",
"description": "我是耗材类别",
"level": 1,
"parentId": null,
"href": "/category/categoryview",
"target": "_self",
"createTime": "2023-10-17T10:05:50.1467756"
},
{
"id": "50814ed2-1c2b-4ee6-96ee-58a91b442632",
"title": "耗材信息",
"description": "我是耗材信息",
"level": 1,
"parentId": null,
"href": "/consumableinfo/consumableinfoview",
"target": "_self",
"createTime": "2023-10-16T17:25:27.1296662"
},
{
"id": "2d461579-fa97-4be9-91bb-4faf0c8be26c",
"title": "角色管理",
"description": "我是角色管理",
"level": 1,
"parentId": null,
"href": "/roleinfo/RoleinfoView",
"target": "_self",
"createTime": "2023-10-13T17:30:14.7072381"
},
{
"id": "3623d0de-6ff5-4256-b550-d4410aea7664",
"title": "部门管理",
"description": "我是部门管理",
"level": 1,
"parentId": null,
"href": "/department/departmentinfoview",
"target": "_self",
"createTime": "2023-10-13T17:28:48.4932763"
},
{
"id": "22b2ea54-05cc-4b61-889c-57b2eb997aec",
"title": "用户管理",
"description": "我是用户管理",
"level": 1,
"parentId": null,
"href": "/userinfo/TableListView",
"target": "_self",
"createTime": "2023-10-13T17:27:36.252836"
}
],
"count": 13
}
在后端实现递归菜单时,有几个注意事项需要考虑:
-
数据库设计: 递归菜单通常通过数据库中的父子关系来表示。在数据库中,你可以添加一个字段来存储父菜单的ID。这样,你可以通过遍历和递归查询来构建菜单层次结构。
-
循环引用和死循环: 在设计递归菜单时,要注意防止出现循环引用或死循环的情况。这种情况发生在菜单A的父菜单是菜单B,而菜单B的父菜单又是菜单A。当你进行查询和构建菜单层次结构时,要确保没有出现循环引用或死循环,否则可能导致无限循环或数据错误。
-
查询和构建菜单: 递归菜单需要进行递归查询和构建。你可以使用递归函数来处理这个过程。在数据库查询中,根据父菜单的ID来查询子菜单,并递归调用函数来获取子菜单的子菜单。然后,你可以将递归查询的结果转换为适当的数据结构,例如树形结构或JSON格式。
-
性能优化: 如果菜单层次结构非常大或存在多级递归,请注意性能问题。对于大型菜单,递归查询可能会导致性能下降。你可以使用一些优化技巧,例如使用缓存、限制递归深度或引入其他数据结构来提高性能。
-
前端处理: 在后端构建递归菜单后,前端需要相应地处理和展示菜单。你可以使用递归组件或迭代方式来渲染菜单。确保前端能够正确处理菜单的层次结构和子菜单。
在实现和使用递归菜单时,请考虑上述注意事项,并确保在设计、查询和展示菜单时遵循最佳实践。
在将递归菜单传递给前端的过程中,有几个注意事项需要考虑:
序列化为JSON: 前端通常使用JSON格式来处理数据。在将递归菜单传递给前端之前,你需要确保将其序列化为有效的JSON格式。你可以使用JSON序列化库,如Newtonsoft.Json来将数据对象转换为JSON字符串。
var recursiveMenuJson = JsonConvert.SerializeObject(recursiveMenu);
循环引用检测: 当递归菜单存在循环引用时,序列化可能会导致无限循环。为了避免这种情况,你可以使用循环引用检测功能。在Newtonsoft.Json库中,你可以通过设置ReferenceLoopHandling属性来处理循环引用。
var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; var recursiveMenuJson = JsonConvert.SerializeObject(recursiveMenu, settings);
-
处理空子菜单: 在递归菜单中,某些菜单项可能没有子菜单。在序列化过程中,你需要确保将空的子菜单设置为null,以避免在前端处理期间出现问题。
-
前端递归处理: 在前端,你需要根据递归菜单的结构来进行递归处理,以正确渲染菜单的层次结构。使用递归方法或模板来遍历菜单项,并将菜单项的子菜单作为嵌套元素进行处理。
下面是一个示例,在前端使用JavaScript递归处理递归菜单的代码:
function renderMenu(menuItems, parentElement) { var menuList = document.createElement('ul'); menuItems.forEach(function (menuItem) { // 创建菜单项 var listItem = document.createElement('li'); var link = document.createElement('a'); link.href = menuItem.Url; link.textContent = menuItem.Name; listItem.appendChild(link); // 递归处理子菜单 if (menuItem.SubMenus) { renderMenu(menuItem.SubMenus, listItem); } // 添加到菜单列表 menuList.appendChild(listItem); }); parentElement.appendChild(menuList); } // 假设在HTML页面中存在一个id为"menu"的元素,用于渲染菜单 var menuContainer = document.getElementById('menu'); renderMenu(recursiveMenu, menuContainer);
此代码示例将递归菜单项渲染为多级无序列表,并使用递归方法在HTML页面中生成菜单结构。
在Vue.js中实现递归菜单,你可以按照以下步骤进行操作:
定义菜单组件: 首先,你需要定义一个菜单组件,用于渲染菜单项以及递归渲染子菜单。该组件将接收一个菜单项对象作为参数,并根据该菜单项的属性进行渲染。
<template>
<li>
<a :href="menuItem.url">{{ menuItem.name }}</a>
<ul v-if="menuItem.subMenus.length > 0">
<menu-item
v-for="subMenu in menuItem.subMenus"
:menu-item="subMenu"
:key="subMenu.id"
></menu-item>
</ul>
</li>
</template>
<script>
export default {
name: 'MenuItem',
props: {
menuItem: {
type: Object,
required: true
}
}
}
</script>
在上面的示例中,MenuItem组件根据菜单项对象的属性进行渲染。如果菜单项包含子菜单,MenuItem组件将递归地渲染子菜单项。
使用递归组件: 在父组件中使用递归调用方式使用MenuItem组件来渲染递归菜单。父组件将传递菜单数据作为属性给递归组件。
<template>
<ul>
<menu-item
v-for="menu in menus"
:menu-item="menu"
:key="menu.id"
></menu-item>
</ul>
</template>
<script>
import MenuItem from './MenuItem.vue';
export default {
name: 'Menu',
components: {
MenuItem
},
data() {
return {
menus: [] // 菜单数据源
};
},
// 在这里获取菜单数据
mounted() {
this.fetchMenus();
},
methods: {
fetchMenus() {
// 从API或其他数据源获取菜单数据
// 将菜单数据保存到this.menus中
}
}
}
</script>
在父组件中,你可以调用API或使用其他方式获取菜单数据,并将其保存在父组件的menus属性中。然后,使用v-for指令循环遍历菜单数据,并将每个菜单项传递给MenuItem组件进行渲染。
引入菜单组件: 在主应用程序文件中引入父组件,并在合适的位置使用该组件来渲染菜单。
<template>
<div id="app">
<menu></menu> <!-- 使用自定义的菜单组件 -->
<!-- 其他应用程序内容 -->
</div>
</template>
<script>
import Menu from './Menu.vue';
export default {
components: {
Menu
}
}
</script>
在主应用程序文件中,你需要将菜单组件引入,并在适当的位置使用该组件来渲染菜单。
这样,当应用程序加载时,Vue.js将从API或其他数据源获取菜单数据,并使用递归菜单组件渲染菜单的层次结构。递归菜单组件将根据数据的嵌套层次自动进行递归渲染。
本文介绍了如何在ASP.NET Core中构建递归菜单,包括设计菜单模型,使用Entity Framework Core查询数据库,递归处理菜单数据,以及在前端使用JavaScript或Vue.js进行递归渲染。同时,提到了注意事项,如防止循环引用,优化性能以及序列化为JSON的处理。
2027

被折叠的 条评论
为什么被折叠?



