递归生成树形(无限级)菜单

1、什么是递归?

递归,简单的来说就是按照某种对应的规则,一直遍历循环,直到结束为止。在应用程序中,更直白的来说,就是自己调用自己。在实际应用中,我们会发现,按照常规的思维想要得到树形(无限级)菜单,根本是不可能的事,循环、遍历、赋值?好像都无法实现。
那么这时,借助我们的递归思想和方法,就将这种不可能变成了可能,把这种复杂变得好像就是这么简单?对,这就是递归,一种突破常规思维的思想,明白它、理解它、应用它,一定会受益无穷!

2、设计表

这里需要说明一下,其实整张表最重要的三个字段呢,我已经在注释中标注出来了,即主键、父节点(上级)、名称,其他的字段都是我在应用的时候附加的,作为练习和理解递归的话,这三个字段已足够。

CREATE TABLE [dbo].[Menu](
	[Id] [nvarchar](200) NOT NULL,/*主键*/
	[Title] [nvarchar](200) NULL,/*名称*/
	[ParentId] [nvarchar](200) NULL,/*父节点*/
	[Url] [nvarchar](200) NULL,
	[Icon] [nvarchar](200) NULL,
	[TabId] [int] IDENTITY(1,1) NOT NULL,
	[IsOpen] [bit] NOT NULL,
	[HaveChild] [bit] NOT NULL,
	[SorTime] [datetime] NOT NULL,
 CONSTRAINT [PK_MP_Menu] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

3、使用SQL语句递归:

这里创建成存储过程,方便复用。提供四种递归作为参考:

(1)、获取直属父节点

/*获取该节点的直属父节点*/
CREATE procedure [dbo].[GetParent]
@id nvarchar(200)
as
select m2.* from Menu m1,Menu m2 where m1.parentid=m2.id and m1.id=@id

(2)、获取全部父节点

/*获取该节点的所有父节点*/
CREATE procedure [dbo].[GetParentList]
@id nvarchar(200)
as
with tree as(
select m2.* from Menu m1,Menu m2 where m1.parentid=m2.id and m1.id=@id
union all
select m.* from Menu m,tree t where m.id=t.parentid)
select * from tree;

(3)、获取直属子节点

/*获取该节点的直属子节点*/
CREATE procedure [dbo].[GetNode]
@id nvarchar(200)
as
select m.* from Menu m where m.parentid=@id

(4)、获取全部子节点

/*获取该节点的所有子节点*/
CREATE procedure [dbo].[GetNodeList]
@id nvarchar(200)
as
with tree as(
select m.* from Menu m where m.parentid=@id
union all
select m.* from Menu m,tree t where m.parentid=t.id )
select * from tree;

4、在程序代码中体现:

这里最重要的就是明白递归中,自己调用自己的意义了,很直白的说,就好像子节点的子节点还有子节点,那么我们就要调用直到子节点的子节点没有子节点为止,就是这么个道理,理解了就相当easy,不理解就多理解理解,没有别的办法!

        /// <summary>
        /// 递归树形节点
        /// </summary>
        /// <returns></returns>
        public List<MenuModel> GetTreeList()
        {            
            var queryData = menu.GetEntity(a => a.ParentId.Equals(null));
            List<MenuModel> mList = (from r in queryData select new MenuModel() 
            {
                id = r.Id,
                title = r.Title,
                parentId = r.ParentId,
                url = r.Url,
                icon = r.Icon,
                tabId = r.TabId,
                isOpen = r.IsOpen,
                haveChild = r.HaveChild,
                sorTime = r.SorTime
            }).ToList();           
                                  
            foreach (var item in mList)
            {
                //递归子节点
                item.nodes = GetNodesList(item);
            }
            
            return mList;
        }

       /// <summary>
        /// 递归子节点
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public List<MenuModel> GetNodesList(MenuModel model)
        {
            var queryData = menu.GetEntity(a => a.ParentId == model.id);
            List<MenuModel> mList = (from r in queryData
                                    select new MenuModel()
                                    {
                                        id = r.Id,
                                        title = r.Title,
                                        parentId = r.ParentId,
                                        url = r.Url,
                                        icon = r.Icon,
                                        tabId = r.TabId,
                                        isOpen = r.IsOpen,
                                        haveChild = r.HaveChild,
                                        sorTime = r.SorTime
                                    }).ToList();
            foreach (var item in mList)
            {
                //递归
                item.nodes = GetNodesList(item);
            }
            return mList.OrderBy(a => a.sorTime).ToList();
        }

对了,这里补充一下,创建的模型:

    public class MenuModel
    {
        /// <summary>
        /// 主键id
        /// </summary>
        public string id { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        public string title { get; set; }

        /// <summary>
        /// 父节点
        /// </summary>
        public string parentId { get; set; }

        /// <summary>
        /// layui绑定tab标识(自增)
        /// </summary>
        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public int tabId { get; set; }

        /// <summary>
        /// 访问地址
        /// </summary>
        public string url { get; set; }

        /// <summary>
        /// 小图标
        /// </summary>
        public string icon { get; set; }

        /// <summary>
        /// 是否(默认展开)
        /// </summary>
        public bool isOpen { get; set; }

        /// <summary>
        /// 是否还有(子节点)标识
        /// </summary>
        public bool haveChild { get; set; }

        /// <summary>
        ///时间(排序)
        /// </summary>
        public DateTime sorTime { get; set; }

        /// <summary>
        /// 是否已分配(角色分配权限时才有意义)
        /// </summary>
        public bool isChecked { get; set; }

        /// <summary>
        /// 子节点
        /// (注:与表结构不同的属性,必须添加不映射注解)
        /// </summary>
        [NotMapped]
        public List<MenuModel> nodes { get; set; }
    }

5、Web层,返回一组Json数据即可:

        /// <summary>
        /// 获取树形节点
        /// </summary>
        /// <returns></returns>
        //[HttpPost]
        public JsonResult GetTreeList()
        {
            return Json(menu.GetTreeList());
        }

返回Json格式如下:

在这里插入图片描述6、效果图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值