Asp.Net Core进行递归菜单返回给前端

本文介绍了如何在ASP.NET Core中构建递归菜单,包括设计菜单模型,使用Entity Framework Core查询数据库,递归处理菜单数据,以及在前端使用JavaScript或Vue.js进行递归渲染。同时,提到了注意事项,如防止循环引用,优化性能以及序列化为JSON的处理。

数据库表格的设计

菜单的实体类

定义菜单数据模型: 首先,你需要定义一个菜单数据模型来表示菜单项。该模型应当包含菜单的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
}

在后端实现递归菜单时,有几个注意事项需要考虑:

  1. 数据库设计: 递归菜单通常通过数据库中的父子关系来表示。在数据库中,你可以添加一个字段来存储父菜单的ID。这样,你可以通过遍历和递归查询来构建菜单层次结构。

  2. 循环引用和死循环: 在设计递归菜单时,要注意防止出现循环引用或死循环的情况。这种情况发生在菜单A的父菜单是菜单B,而菜单B的父菜单又是菜单A。当你进行查询和构建菜单层次结构时,要确保没有出现循环引用或死循环,否则可能导致无限循环或数据错误。

  3. 查询和构建菜单: 递归菜单需要进行递归查询和构建。你可以使用递归函数来处理这个过程。在数据库查询中,根据父菜单的ID来查询子菜单,并递归调用函数来获取子菜单的子菜单。然后,你可以将递归查询的结果转换为适当的数据结构,例如树形结构或JSON格式。

  4. 性能优化: 如果菜单层次结构非常大或存在多级递归,请注意性能问题。对于大型菜单,递归查询可能会导致性能下降。你可以使用一些优化技巧,例如使用缓存、限制递归深度或引入其他数据结构来提高性能。

  5. 前端处理: 在后端构建递归菜单后,前端需要相应地处理和展示菜单。你可以使用递归组件或迭代方式来渲染菜单。确保前端能够正确处理菜单的层次结构和子菜单。

在实现和使用递归菜单时,请考虑上述注意事项,并确保在设计、查询和展示菜单时遵循最佳实践。

在将递归菜单传递给前端的过程中,有几个注意事项需要考虑:

序列化为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);
  1. 处理空子菜单: 在递归菜单中,某些菜单项可能没有子菜单。在序列化过程中,你需要确保将空的子菜单设置为null,以避免在前端处理期间出现问题。

  2. 前端递归处理: 在前端,你需要根据递归菜单的结构来进行递归处理,以正确渲染菜单的层次结构。使用递归方法或模板来遍历菜单项,并将菜单项的子菜单作为嵌套元素进行处理。

下面是一个示例,在前端使用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或其他数据源获取菜单数据,并使用递归菜单组件渲染菜单的层次结构。递归菜单组件将根据数据的嵌套层次自动进行递归渲染。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值