vant树型菜单多级_无限层级菜单—左右值树型数据结构

在上一篇博客中,我提到了后台菜单的问题。其实我不想写,因为比较久了,都差不多忘了,只记得当时理解得很痛苦。下面这个菜单是一个多层级菜单的,在 计算机中心 菜单下,有6个子菜单,在子菜单 微信管理 下面又有3个子菜单,子菜单 企业号管理 又有2个子菜单,层次可以无限地增加,而且层级也是不固定的。那像这样的菜单结构在数据库应该怎么存储呢。树!没错,就是用树型数据结构,在之前的博客中,我也有写过对树型数...
摘要由CSDN通过智能技术生成

在上一篇博客中,我提到了后台菜单的问题。其实我不想写,因为比较久了,都差不多忘了,只记得当时理解得很痛苦。

下面这个菜单是一个多层级菜单的,在 计算机中心 菜单下,有6个子菜单,在子菜单 微信管理 下面又有3个子菜单,子菜单 企业号管理 又有2个子菜单,层次可以无限地增加,而且层级也是不固定的。

那像这样的菜单结构在数据库应该怎么存储呢。

树!没错,就是用树型数据结构,在之前的博客中,我也有写过对树型数据结构相关的想法(树型数组库结构设计),主要用 parent_id 字段记录父节点 id 值形成树成关系,为了方便查询不用使用递归,添加一个 parent_ids 辅助字段来记录节点的全部上级节点 id 。

这次我不想再用这种数据结构了,想尝试一下网上介绍的左右值的树型数据结构。

左右值意义

先看一下这张图

如果你一眼就看出每个数字的意义,那请受我一拜~~~

这些菜单上左右两边的黑色数字就分别是节点的左右值了

请沿着根菜单的左边开始数起(1),往计算机中心的左边(2),再设备管理左边(3),设备管理右边(4),微信管理左边(5),服务号左边(6)。。。大家是否发现了规律。

把每个节点左右都当成两个连接点,从根菜单左边开始,沿着整个树的节点外围连接,一直连回到根菜单的右边,所经过节点的顺序就是节点的左右值了。

(不知这样表达大家是否能理解,我能想到的最通俗的表达也是这样了,不管,我就当是理解了)

很好,大家都理解了。

那我上一下菜单的数据库表图

这里面还添加了一个 level 字段,这个大家都知道,这是节点的层级,根菜单的层级是0。

查询

让大家见识一下这种数据结构的魅力,你会发现查询变得如此的简单,曾经复杂的子孙节点查询,现在变得如果简单。

查询某一节点下面的全部子孙节点

Transact-SQL

//例如想查询第一张图上的计算机中心这个菜单底下全部的子菜单

//计算机中心的左值为2 右值为11 level 为 1

//查询节点下全部子孙菜单不包括自身菜单

SELECT * FROM menu WHERE l > 2 AND r < 11

//查询节点下全部子孙菜单包括自身菜单

SELECT * FROM menu WHERE l >= 2 AND r <= 11

//如果你只想查询子节点,孙节点不想查询到,那么在 where 条件 添加 level = 2

1

2

3

4

5

6

7

8

9

10

//例如想查询第一张图上的计算机中心这个菜单底下全部的子菜单

//计算机中心的左值为2右值为11level为1

//查询节点下全部子孙菜单不包括自身菜单

SELECT*FROMmenuWHEREl>2ANDr<11

//查询节点下全部子孙菜单包括自身菜单

SELECT*FROMmenuWHEREl>=2ANDr<=11

//如果你只想查询子节点,孙节点不想查询到,那么在where条件添加level=2

计算某一节点下有多少个子孙节点

例如想查询第一张图上的计算机中心这个菜单底下有多少个子菜单

计算机中心的左值为2 右值为11。

子菜单数量(不包括自身节点)

( 右值 – 左值 – 1 ) / 2  => ( 11 – 2 – 1 ) / 2 = 4

子菜单数量(包括自身节点)

( 右值 – 左值 + 1 ) / 2  => ( 11 – 2 + 1) / 2 = 5

节点排序

当你在 sql 查询时排序条件加上 order by l 的话,你会发现,返回的节点是按节点全部展开后由上往下的顺序,这个非常有用,在下面的 菜单跟树表格中非常关键。

查询某一节点的全部父祖节点

Transact-SQL

例如想查询第一张图上的 企业号 菜单的有哪些父级菜单

企业号 的左值为8 右值为9 level 为 1。

SELECT * FROM menu WHERE l < 8 AND r > 9 order by l

//返回菜单并按由上往下排序,分别为根菜单,计算机中心,微信管理

//如果想由下往上就把排序条件改成 order by r

//如果你只是想查询某一节点到某父节点经过的路径的话

//例如想查询 企业号 到 计算机中心 经过的菜单

SELECT * FROM menu WHERE l < 8 AND l > 2 AND r > 9 AND r < 11 order by l

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

例如想查询第一张图上的企业号菜单的有哪些父级菜单

企业号的左值为8右值为9level为1。

SELECT*FROMmenuWHEREl<8ANDr>9orderbyl

//返回菜单并按由上往下排序,分别为根菜单,计算机中心,微信管理

//如果想由下往上就把排序条件改成orderbyr

//如果你只是想查询某一节点到某父节点经过的路径的话

//例如想查询企业号到计算机中心经过的菜单

SELECT*FROMmenuWHEREl<8ANDl>2ANDr>9ANDr<11orderbyl

判断

判断某一节点是否是某一节点的子节点

假如我想查询企业号menu1(左:8,右:9)是否是设备管理menu2(左:3,右:4)的子菜单。

判断式: menu1.l > menu2.l AND menu1.r < menu2.r

8 > 3 AND 9 <4 = false

说明企业号不是设备管理的子菜单

判断某一节点是否是某一节点的父节点

假如我想查询计算机中心menu1(左:2,右:11)是否是服务号menu2(左:6,右:7)的父菜单。

判断公式: menu1.l < menu2.l AND menu1.r > menu2.r

2 < 6 AND 11 > 7 = true

说明计算机中心是服务号的父菜单

判断某一节点是否有子菜单

将节点的右值减去左值,如果大于1则说明有。

相信以上的查询及判断已经可以满足对数据的日常使用了,那下面就来一个实际例子,就是我上个博客说到的 AdminLTE 后台菜单。

根据 AdminLTE 模板的 html 结构,以我第一个图为例,那生成的 html 代码如下所示(菜单不包括根菜单)

XHTML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

计算机中心

设备管理

微信管理

服务号

企业号

行政中心

车辆管理

人力资源

组织结构

员工档案

一个菜单就是一个 li ,如果菜单有子菜单的话,就得加上 treeview 的 css 样式,然后在里面嵌套一个 treeview-menu 样式的 ul ,再写上菜单 li ,以此类推,然后哪个菜单是当前打开的就在其 li 上及全部上级菜单 li 加上 active 样式。

我需要拼结的就是

基于thinkphp框架代码

PHP

public function getMenu(){

$admin_user=session("admin_user");

//如果当前用户不是超级管理员

if(-1!=$admin_user["id"]){

$role_id=$admin_user["role_id"];

$arr_menu_id=M("roleMenu")->where(array("role_id"=>$role_id))->getField("menu_id",true);

if(!$arr_menu_id){

return;

}

//到数据库查询权限对应菜单

$arr_menu=M()->table("menu m1,menu m2")->distinct(true)->field("m1.*")->where(array(

"m2.id"=>array("IN",$arr_menu_id),

"m1.l"=>array("EXP","<=m2.l"),

"m1.r"=>array("EXP",">=m2.r"),

"m2.r-m2.l"=>array("EXP","=1"),

"m1.level"=>array("gt","0")

))->order("m1.l")->select();

}else{

//超级管理员直接获取全部菜单

$arr_menu=D("Menu")->where(array("level"=>array("gt","0")))->order("l")->select();

}

//上面是根据当前登录用户获取对应的菜单,大家可以不用理会,直接当成是超级管理员获得全部菜单

//在数组中查找当前的active的菜单

$current_menu;

$controller=CONTROLLER_NAME;//获取当前的控制器

if($controller!=="Index"){

foreach ($arr_menu as $m) {

if(CONTROLLER_NAME===$m[

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vant的`TreeSelect`组件可以实现多级菜单的选择,通过传入树形结构的数据来生成多级菜单。以下是一个使用`TreeSelect`组件实现多级菜单的示例代码: ```html <template> <van-tree-select :items="items" :main-active-index="mainActiveIndex" :active-id="activeId" @item-click="onItemClick" @nav-click="onNavClick" /> </template> <script> export default { data() { return { items: [ { text: '选项1', id: 1, children: [ { text: '选项1-1', id: 11, children: [ { text: '选项1-1-1', id: 111 }, { text: '选项1-1-2', id: 112 } ] }, { text: '选项1-2', id: 12, children: [ { text: '选项1-2-1', id: 121 }, { text: '选项1-2-2', id: 122 } ] } ] }, { text: '选项2', id: 2, children: [ { text: '选项2-1', id: 21, children: [ { text: '选项2-1-1', id: 211 }, { text: '选项2-1-2', id: 212 } ] }, { text: '选项2-2', id: 22, children: [ { text: '选项2-2-1', id: 221 }, { text: '选项2-2-2', id: 222 } ] } ] } ], mainActiveIndex: 0, activeId: '' }; }, methods: { onItemClick(item) { this.activeId = item.id; }, onNavClick(index) { this.mainActiveIndex = index; } } }; </script> ``` 在这个示例中,我们传入了一个树形结构的数据数组`items`,每个节点包含了`text`和`id`属性,以及可选的`children`属性。在`TreeSelect`组件中,我们设置了`items`属性来传入数据,同时监听了`item-click`和`nav-click`事件来处理选中的节点和导航栏的点击。在模板中,我们使用了`main-active-index`和`active-id`属性来控制导航栏和选中的节点。 需要注意的是,由于`TreeSelect`组件的数据格式比较特殊,如果需要从其他数据源中获取数据,可能需要对数据进行转换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值