【商城后台管理系统】基于TP6开发角色的权限分配递归生成无限极权限菜单树
角色的新增修改删除页面在开发中和其他功能一样都是进行添加删除修改等操作,但是唯一难点在于我们要对不同角色进行权限分配问题,要从权限菜单数据库表中读取出无限极菜单数据,从而遍历在内存中生成无限极的菜单树结构。
在了解如何生成并渲染无限极菜单树之前,我们先看一下地址引用和递归吧。
PHP 递归几种方法
以a<10作为判断条件,条件成立,则把a赋给result[];将result的引用传入函数,会将每一次递归产生的a添加到结果数组result。因而本例生成的$result数组是 Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 7 [7] => 8 [8] => 9 ) 。
本例比较有意思的是echo a的值。相信很多人认为是12345678910吧,其实不然,是1098765432。为什么呢?因为函数还没执行echoa前就进行了下一次的函数递归。真正执行echo a是当a<10条件不满足的时候,echo a,返回result,对于上一层而言,执行完递归函数,开始执行本层的echo $a,依次类推。
一、利用静态变量的方法
我们常常在类中见到static,今天我们把它利用到递归函数中。请记住static的作用:仅在第一次调用函数的时候对变量进行初始化,并且保留变量值。如:
function test(){static $count=0;echo $count;$count++;}test();test();test();test();
请问这一段代码的执行结果是多少?是00000么?必然不是。是01234。首先第一次调用test(),static对 $count 进行初始化,其后每一次执行完都会保留 $count 的值,不再进行初始化,相当于直接忽略了 static $count=0; 这一句。
因而将static应用到递归函数作用可想而知。在将需要作为递归函数间作为“桥梁”的变量利用static进行初始化,每一次递归都会保留”桥梁变量”的值。所以,这种方式实现的递归,只能在一次请求中,被调用,如果多次调用,则会将以前的值累加到最后一次调用中。
<?phpfunction call(){ static $i = 0; echo $i . ''; $i++; if($i<10){ call(); }}call();
输出:
0 1 2 3 4 5 6 7 8 9
二、通过全局变量的方法
利用全局变量完成递归函数,请确保你确实理解什么是全局变量。global在函数内申明变量不过是外部变量的同名引用。变量的作用范围仍然在本函数范围内。改变这些变量的值,外部同名变量的值自然也改变了。但一旦用了&,同名变量不再是同名引用。利用全局变量实现递归函数没必要理解到这么深的一层,还保持原有对全局变量的看法就可以顺理成章理解递归函数。
function test($a=0,$result=array()){ global $result; $a++; if ($a<10) { $result[]=$a; test($a,$result); } return $result;}
三、通过引用传参的方式
可以利用引用变量来解决某些问题(变量名前 加上 & 符号)
PHP 的引用允许用两个变量来指向同一个内容,例如 $a = &$b; 这意味着 $a 和 $b 指向了同一个变量。
如下例子,因为 $result使用了引用传递,所以数据会一直累加。
<?phpfunction test($a=0,&$result=array()){ $a++; if ($a<10){ $result[]=$a; test($a,$result); } echo $a."<hr>"; return $result;}var_dump(test());
总结
所谓递归函数,重点是如何处理函数调用自身是如何保证所需要的结果得以在函数间合理”传递”,当然也有不需要函数之间传值得递归函数,例如:
function test($a=0){ $a++; if ($a<10) { echo $a; test($a); }}
面对这样的函数,我们就不必大伤脑筋了。顺便说一句,深入理解变量引用相关知识对解决这类问题大有裨益。
最后给大家分享一个php实现递归与无限分类的方法,具体实现方法如下:
<?php echo "<pre>"; $area = array( array('id'=>1,'area'=>'北京','pid'=>0), array('id'=>2,'area'=>'广西','pid'=>0), array('id'=>3,'area'=>'广东','pid'=>0), array('id'=>4,'area'=>'福建','pid'=>0), array('id'=>11,'area'=>'朝阳区','pid'=>1), array('id'=>12,'area'=>'海淀区','pid'=>1), array('id'=>21,'area'=>'南宁市','pid'=>2), array('id'=>45,'area'=>'福州市','pid'=>4), array('id'=>113,'area'=>'亚运村','pid'=>11), array('id'=>115,'area'=>'奥运村','pid'=>11), array('id'=>234,'area'=>'武鸣县','pid'=>21) ); function t($arr,$pid=0,$lev=0){ static $list = array(); foreach($arr as $v){ if($v['pid']==$pid){ echo str_repeat(" ",$lev).$v['area']."<br />"; //这里输出,是为了看效果 $list[] = $v; t($arr,$v['id'],$lev+1); } } return $list; } $list = t($area); echo "<hr >"; print_r($list);?>
所以一般来讲我总结递归函数有两个特点:
一个是记录条件值,记录的条件值必须保证不会再下一次调用时丢失。
test函数里$a便是记录条件值,它是依靠使用static关键字来保证记录每次增加的数值不会再下一次调用test()函数而丢失,因为函数中static修饰的变量仅仅在第一次初始化,并保留变量值。所以只要保证这一点,不光static,其他的方式也可以达到目的,例如global还有&修饰符。
另一个是条件检查。test里面对$a大小的限制就是该条件的检查过程。
test函数中if($a<10)就是这个条件检查的过程,它限制了test()函数对自身的调用,这样就可以防止无限调用导致程序奔溃。
函数a内部调用另外的函数b,如果b函数没有完成,那么a函数就会一直等待下去,直到b函数完成,才会回到a函数继续执行。递归的过程中利用了这个特性,正是这个能帮助我们对无限分类进行排序,用上面test()递归函数说明:
上面test()打印出来的结果是:
10,10,10,10,10,10,10,10,10,10,10
可能和很多人想的不一样,大概有许多人会觉得不应该是0,1,2,3,4,5,6,7,8,9,10这样的结果吗?
这是因为函数test调用过程中,只要$a<10,就会调用自身的test(),每次调用的test()都没有到达echo处,也就是每次调用的test函数并没有完结,直到$a递增到了10,才第一次echo $a,这个时候$a已经是10了,因此第一个10实际是递归了11次后的$a,第十一次递归的test由于不符合<10的条件该函数完结,这个时候才开始回到递归第十次test函数里执行echo,这个时候由于$a是静态变量,值已经是10了,因此echo出的结果是10,下面依次回到之前的test函数完成前面未完成的echo步骤,因此echo出11个10,最后一个10实际是第一次执行test函数的echo结果。
无限分类的排序完成也是用的这个原理,递归排序函数不断的通过parentid等于上一级id的子类来匹配该类别的子类别,只要找到第一个子类,就用找到的这个子类的id去找下一级的子类,直到没有更下级的子类的时候,才返回上一级接着继续找,找到后又开始寻找该子类下一级子类,直到没有为止才返回,这个过程不断循环。可能用文字不太能理解,下面的实例中我会画出图例,请先往后看。
我们先来看无限分类的数据er图:
更多介绍请查看:
无限极分类(adjacency list)的三种方式(迭代、递归、引用)
好言归正传,回归到我们自己的项目中。
新建控制器Group.php
<?phpnamespace app\admin\controller;use app\BaseController;use think\facade\Session;use think\facade\View;use think\facade\Db;use think\facade\Cache;use think\facade\Request;use app\admin\controller\Base;/** * 角色管理 */class Group extends Base{ // 菜单列表 public function index(){ $data['group'] = Db::table('admin_group')->select(); return view('group/index',$data); }}
view/Group/index.php 角色列表渲染视图
<!DOCTYPE html><html><head> <title></title> <link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css"> <script type="text/javascript" src="/static/layui/layui.js"></script></head><body style="padding: 20px;"> <div class="layui-card"> <div class="layui-card-header"><h3>管理员列表</h3></div> <div class="layui-card-body"> <span class="layui-breadcrumb"> <a>管理账号</a> <a href="/admin/admin/index"><cite>账号列表</cite></a> </span> <button style="float: right;" type="button" class="layui-btn layui-btn-normal layui-btn-radius layui-btn-sm" onclick="add()"> <i class="layui-icon layui-icon-add-1"></i>添加 </button> <hr class="layui-border-blue"> <table class="layui-table" > <thead> <tr> <th>ID</th> <th>用户名</th> <th>角色</th> <th>真实姓名</th> <th>电话</th> <th>添加时间</th> <th>登陆时间</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> {foreach $data['adminList'] as $user} <tr> <td>{$user['id']}</td> <td>{$user['username']}</td> <td>{$user['group_title']}</td> <td>{$user['truename']}</td> <td>{$user['phone']}</td> <td>{$user['add_time'] | date='Y-m-d H:i:s'}</td> <td>{$user['lastlogin'] | date='Y-m-d H:i:s'}</td> <td> {if($user['status']==0)} <span class="layui-badge layui-bg-green"><i class="layui-icon layui-icon-ok-circle"></i>正常</span> {else if/} <span class="layui-badge layui-bg-red"><i class="layui-icon layui-icon-close"></i></i>禁用</span> {/if} </td> <td> <button type="button" class="layui-btn layui-btn-xs" onclick="edit({$user['id']})"> <i class="layui-icon layui-icon-edit"></i>修改 </button> <button type="button" class="layui-btn layui-btn-xs layui-bg-red" onclick="del({$user['id']})"> <i class="layui-icon layui-icon-delete"></i>删除 </button> </td> </tr> {/foreach} </tbody> </table> </div> </div></body></html><script> $ = layui.jquery; function add(){ layer.full(layer.open({ type: 2, title:'添加管理员', shadeClose: false, maxmin: true, shade:0.8, area:['480px','450px'], content:'/admin/admin/add' })); } function edit(id){ layer.open({ type: 2, title:'修改管理员信息', shadeClose: false, shade:0.8, area:['480px','450px'], content:'/admin/admin/edit?id='+id }); } function del(id){ layer.confirm('确定要删除吗?',{ icon:3, btn: ['确定','取消'] },function(){ $.post('',{'id':id},function(res){ if(res.code>0){ layer.alert(res.msg,{icon:2}); }else{ layer.msg(res.msg); setTimeout(function(){window.location.reload();},1000); } },'json'); }); }</script>
很简单,就是读出数据渲染一下,实现效果:
下面我们来看添加角色列表功能,这里主要是给角色分配权限菜单的复选框渲染,我们采用二级渲染方式,而不是无限极的递归树,这样不便于用户使用查看,我们先将数据库中读出的权限菜单数组,构造成那种无限极的菜单树,然后在利用递归的方式,将子菜单下的子菜单都递归放到顶级菜单的children下面。
我们来看一下直接从数据库中读取,然后循环渲染效果吧。
新建添加视图页面view/Group/add.php
<!DOCTYPE html><html><head> <title></title> <link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css"> <script type="text/javascript" src="/static/layui/layui.js"></script> <style type="text/css"> .Menu{box-shadow: 5px 5px 16px #e2dcdc;padding: 8px;margin-bottom: 15px;} .Menu:hover{background: #e0dcdc26} .childMenu{border: 1px dashed #9e9e9e96;padding: 0px 0 8px 20px;margin: 8px;} .childMenu:hover{background: #e0dcdc26} </style></head><body> <div class="layui-form" style="padding: 10px;"> <form class="layui-form" method="post"> <div class="layui-form-item"> <label class="layui-form-label">角色名称</label> <div class="layui-input-inline"> <input type="text" class="layui-input" name="title" placeholder="请输入角色名称"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">权限列表</label> <div class="layui-input-block"> <?php foreach ($menus as $key => $item): ?> <input type="checkbox" lay-skin="primary" name="rights[]" value="<?=$item['mid']?>" class="layui-input" title='<?=$item['title']?>'> <?php endforeach ?> </div> </div> </form> <div class="layui-input-block"> <button type="button" class="layui-btn" onclick="save()">保存</button> </div> </div></body></html>
控制器方法add
// 添加角色 public function add(){ if(Request::isPost()){ $data = Request::post(); if(empty(input('post.title'))){ exit(json_encode(array('code'=>1,'msg'=>'请填写角色名称'))); } if(empty(input('post.rights'))){ exit(json_encode(array('code'=>1,'msg'=>'请勾选权限菜单'))); } // 将数组中数据转换为整型 $data['rights'] = array_map('intval',$data['rights']); // 将勾选的权限数组转换为json字符串 $data['rights'] = json_encode($data['rights']); // 保存添加数据 $res = Db::table('admin_group')->save($data); if(!$res){ exit(json_encode(array('code'=>1,'msg'=>'添加失败'))); } exit(json_encode(array('code'=>0,'msg'=>'添加成功'))); }else{ // 查询出所有权限菜单 $tmp_menu_list = Db::table('admin_menu')->where('status',0)->select()->toArray(); // echo "<pre>"; // print_r($tmp_menu_list); return view('group/add',$data); } }
读取出来渲染之后是这样的,显然也不方面操作,没有任何的菜单层次,无法判断他们之前的上下级关系。
我们需要将菜单树改造成如下形式的二级附属关系,相比无限极的树状结构,这样更容易操作和勾选对应主菜单下的权限功能。
内存中构建无限极菜单树演示,将子菜单的内存地址放到children,实际数据依然在原地址中,我们后续对子菜单的子菜单操作,该children数组因为是指向他的内存地址的,所有数据也会随之改变。
构造菜单树 (无限极)
// 构造菜单树 (无限极) // 遍历所有同级菜单,将pid为父级的mid的菜单放到children属性中,依次下去,只要pid为其他菜单的mid就将其放到children属性中,循环往复下去,构造成一个无限极菜单树。 private function buildMenuTree($items){ // 将构造好的数据放到新数组中返回 $tree = array(); // 循环遍历所有菜单 foreach ($items as $item) { // 因为我们将mid作为了菜单数组的下标,直接对比哪个菜单的pid在数组下标中 if(isset($items[$item['pid']])){ // 将pid作为数组下标,即如果是顶级菜单pid=0则该数组未定义, // 如果非顶级菜单 pid=3 就会找到mid=3的对应父级菜单,因为我们的索引就是mid // 为其父级菜单下增加children属性,存放pid等于mid对应的下级菜单地址引用,注意:这里需要是赋值子菜单的地址引用,这样才可以将子的子菜单放到子菜单中,如果只是将第二级子菜单的值放到其中,则无法实现无限极菜单树,children下也多了一份,由于子菜单可能存在多个需要赋值给数组,地址引用,我们只是将该片内存地址赋值给了children下,后续如果这个菜单下面还有子菜单,可以直接映射到同一片内存地址中。从而达到无限极的效果。 $items[$item['pid']]['children'][] = &$items[$item['mid']]; }else{ // 查找不到父级的菜单都放到该数组中 也就是顶级菜单下面已经增加了children子菜单的地址引用,这里不能直接将值赋值过去,因为后续我们可能还有子菜单的子菜单,也许还会往里面添子菜单,地址引用就可以随时在内存中变化,详解请查看PHP值传递和引用传递 $tree[] = &$items[$item['mid']]; } } return $tree; } // 添加角色 public function add(){ if(Request::isPost()){ $data = Request::post(); if(empty(input('post.title'))){ exit(json_encode(array('code'=>1,'msg'=>'请填写角色名称'))); } if(empty(input('post.rights'))){ exit(json_encode(array('code'=>1,'msg'=>'请勾选权限菜单'))); } // 将数组中数据转换为整型 $data['rights'] = array_map('intval',$data['rights']); // 将勾选的权限数组转换为json字符串 $data['rights'] = json_encode($data['rights']); // 保存添加数据 $res = Db::table('admin_group')->save($data); if(!$res){ exit(json_encode(array('code'=>1,'msg'=>'添加失败'))); } exit(json_encode(array('code'=>0,'msg'=>'添加成功'))); }else{ // 查询出所有权限菜单 $tmp_menu_list = Db::table('admin_menu')->where('status',0)->select()->toArray(); // echo "<pre>"; // print_r($tmp_menu_list); // 将索引修改为每个菜单的mid对应值(菜单的mid作为索引下标) $menu_list = []; foreach ($tmp_menu_list as $key => $menu) { // 将每个菜单的mid作为新数组的键名值为该条菜单 $menu_list[$menu['mid']] = $menu; } // echo "<pre>"; // print_r($menu_list); // 调用该方法构造菜单树状结构 $menus = $this->buildMenuTree($menu_list); echo "<pre>"; print_r($menus); $data['menus'] = $menus; return view('group/add',$data); } }
构建完成的无限极菜单树如下所示:
到这里已经构建好了无限极菜单树,如果我们前端渲染为树状结构的话,直接将菜单树数据传递给视图层即可,这里我们还要进一步处理,将无限极的菜单树,递归操作将子菜单的子菜单都放到顶级菜单的children数组中,改造成只有两级菜单关系,只有便于视图层渲染,只要嵌套两层foreach即可,当然无限极的也可以渲染,要利用layui的树状表格进行渲染。便于查看,但这里我们为了方便勾选权限列表,就将其改造为只有两级。顶级菜单下包含各个操作功能进行勾选。
<?phpnamespace app\admin\controller;use app\BaseController;use think\facade\Session;use think\facade\View;use think\facade\Db;use think\facade\Cache;use think\facade\Request;use app\admin\controller\Base;/** * 角色管理 */class Group extends Base{ // 菜单列表 public function index(){ $data['group'] = Db::table('admin_group')->select(); return view('group/index',$data); } // 构造菜单树 (无限极) // 遍历所有同级菜单,将pid为父级的mid的菜单放到children属性中,依次下去,只要pid为其他菜单的mid就将其放到children属性中,循环往复下去,构造成一个无限极菜单树。 private function buildMenuTree($items){ // 将构造好的数据放到新数组中返回 $tree = array(); // 循环遍历所有菜单 foreach ($items as $item) { // 因为我们将mid作为了菜单数组的下标,直接对比哪个菜单的pid在数组下标中 if(isset($items[$item['pid']])){ // 将pid作为数组下标,即如果是顶级菜单pid=0则该数组未定义, // 如果非顶级菜单 pid=3 就会找到mid=3的对应父级菜单,因为我们的索引就是mid // 为其父级菜单下增加children属性,存放pid等于mid对应的下级菜单地址引用,注意:这里需要是赋值子菜单的地址引用,这样才可以将子的子菜单放到子菜单中,如果只是将第二级子菜单的值放到其中,则无法实现无限极菜单树,children下也多了一份,由于子菜单可能存在多个需要赋值给数组 $items[$item['pid']]['children'][] = &$items[$item['mid']]; }else{ // 查找不到父级的菜单都放到该数组中 也就是顶级菜单下面已经增加了children子菜单的地址引用,这里不能直接将值赋值过去,因为后续我们可能还有子菜单的子菜单,也许还会往里面添子菜单,地址引用就可以随时在内存中变化,详解请查看PHP值传递和引用传递 $tree[] = &$items[$item['mid']]; } } return $tree; } // 利用递归处理将所有的子菜单中的子菜单都放到顶级菜单下的children中 private function formatMenus($items,&$res = []){ foreach ($items as $item) { // 出口条件 如果子菜单不存在子菜单children了,不在递归,将数据放到res地址中 if(!isset($item['children'])){ $res[] = $item; }else{ // 临时变量存放子菜单 $temp = $item['children']; // 删除原数组$item中的children子菜单 unset($item['children']); // 将$item赋值给$res存放所有子菜单 $res[] = $item; // 如果存在子菜单children,递归调用自己 $this->formatMenus($temp,$res); } } return $res; } // 权限菜单数据 private function dataMenus(){ // 查询出所有权限菜单 $tmp_menu_list = Db::table('admin_menu')->where('status',0)->select()->toArray(); // echo "<pre>"; // print_r($tmp_menu_list); // 将索引修改为每个菜单的mid对应值(菜单的mid作为索引下标) $menu_list = []; foreach ($tmp_menu_list as $key => $menu) { // 将每个菜单的mid作为新数组的键名值为该条菜单 $menu_list[$menu['mid']] = $menu; } // echo "<pre>"; // print_r($menu_list); // 调用该方法构造菜单树状结构 $menus = $this->buildMenuTree($menu_list); // echo "<pre>"; // print_r($menus); // 再次将无限极菜单树改造成两层的,子菜单全部放到顶级菜单的children下面,便于视图的渲染和展示 $result = []; foreach($menus as $value){ // 如果存在子菜单,我们就调用将子菜单传入递归函数中 if(isset($value['children'])){ $value['children'] = $this->formatMenus($value['children']); }else{ $value['children'] = []; } $result[] = $value; } echo "<pre>"; print_r($result); return $result; } // 添加角色 public function add(){ if(Request::isPost()){ $data = Request::post(); if(empty(input('post.title'))){ exit(json_encode(array('code'=>1,'msg'=>'请填写角色名称'))); } if(empty(input('post.rights'))){ exit(json_encode(array('code'=>1,'msg'=>'请勾选权限菜单'))); } // 将数组中数据转换为整型 $data['rights'] = array_map('intval',$data['rights']); // 将勾选的权限数组转换为json字符串 $data['rights'] = json_encode($data['rights']); // 保存添加数据 $res = Db::table('admin_group')->save($data); if(!$res){ exit(json_encode(array('code'=>1,'msg'=>'添加失败'))); } exit(json_encode(array('code'=>0,'msg'=>'添加成功'))); }else{ $data['menus'] = $this->dataMenus(); return view('group/add',$data); } }}
利用递归,将无限极的子菜单的子菜单全部放到顶级菜单下的children数组中,成为了二维数组菜单
这样就可以将数据传递给视图层进行复选框渲染了。
<!DOCTYPE html><html><head> <title></title> <link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css"> <script type="text/javascript" src="/static/layui/layui.js"></script> <style type="text/css"> .Menu{box-shadow: 5px 5px 16px #e2dcdc;padding: 8px;margin-bottom: 15px;} .Menu:hover{background: #e0dcdc26} .childMenu{border: 1px dashed #9e9e9e96;padding: 0px 0 8px 20px;margin: 8px;} .childMenu:hover{background: #e0dcdc26} </style></head><body> <div class="layui-form" style="padding: 10px;"> <form class="layui-form" method="post"> <div class="layui-form-item"> <label class="layui-form-label">角色名称</label> <div class="layui-input-inline"> <input type="text" class="layui-input" name="title" placeholder="请输入角色名称"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">权限列表</label> <div class="layui-input-block"> <?php foreach ($menus as $key => $item): ?> <div class="Menu"> <input type="checkbox" lay-skin="primary" name="rights[]" value="<?=$item['mid']?>" class="layui-input" title='<span style="font-weight: bolder;"><?=$item['title']?></span><span style="font-size: 11px;color: gray">记得勾选顶级菜单权限哦!</span>' > <hr> <?php if(!empty($item['children'])): ?> <div class="childMenu"> <?php foreach ($item['children'] as $key => $value): ?> <input type="checkbox" lay-skin="primary" name="rights[]" value="<?=$value['mid']?>" class="layui-input" title='<span style="color:#9e9e9e;" ><?=$value['title']?></span>'> <?php endforeach ?> </div> <?php endif ?> </div> <?php endforeach ?> </div> </div> </form> <div class="layui-input-block"> <button type="button" class="layui-btn" onclick="save()">保存</button> </div> </div></body></html><script type="text/javascript"> $ = layui.jquery; function save(){ // 利用form表单提交数据集 会自动将选中的菜单组成数组rights传递 /* $.post('/admin/group/add',$('form').serialize(),function(res){ if(res.code>0){ return layer.alert(res.msg,{icon:2}); } layer.alert(res.msg,{icon:1}); setTimeout(()=>{parent.window.location.reload();},1000); },'json');*/ // 手动获取需要提交的数据 var title = $('input[name="title"]').val(); var checks = $('form input[type="checkbox"]:checked'); // 循环取出每个复选框的菜单mid var rights = []; $.each(checks,(k,v)=>{ rights.push($(v).attr('value')); }); var data = { title:title, rights:rights }; $.post('/admin/group/add',data,function(res){ if(res.code>0){ return layer.alert(res.msg,{icon:2}); } layer.alert(res.msg,{icon:1}); setTimeout(()=>{parent.window.location.reload();},1000); },'json'); }</script>
效果如下:通过勾选复选框进行权限菜单的选择,然后将每个菜单的mid传递给后台接口,进行数据的添加和修改更新操作。
修改页面的数据和添加页面一样,区别就是要先根据id查询出对应角色的rights权限菜单mid组成的数组。然后修改页面通过in_array数组方法查看当前的复选框菜单的mid是否在该角色的rights数组中,如果在就加上checked选中这个复选框。
<?=in_array($item['mid'],$group['rights'])?'checked':'' ?>
修改页面视图代码 view\Group\edit.php
<!DOCTYPE html><html><head> <title></title> <link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css"> <script type="text/javascript" src="/static/layui/layui.js"></script> <style type="text/css"> .Menu{box-shadow: 5px 5px 16px #e2dcdc;padding: 8px;margin-bottom: 15px;} .Menu:hover{background: #e0dcdc26} .childMenu{border: 1px dashed #9e9e9e96;padding: 0px 0 8px 20px;margin: 8px;} .childMenu:hover{background: #e0dcdc26} </style></head><body> <div class="layui-form" style="padding: 10px;"> <form class="layui-form" method="post"> <input type="id" name="id" hidden="" value="<?=$group['id'] ?>"> <div class="layui-form-item"> <label class="layui-form-label">角色名称</label> <div class="layui-input-inline"> <input type="text" class="layui-input" name="title" placeholder="请输入角色名称" value="<?=$group['title']?>"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">权限列表</label> <div class="layui-input-block"> <?php foreach ($menus as $key => $item): ?> <div class="Menu"> <input type="checkbox" lay-skin="primary" name="rights[]" value="<?=$item['mid']?>" class="layui-input" title='<span style="font-weight: bolder;"><?=$item['title']?></span><span style="font-size: 11px;color: gray">记得勾选顶级菜单权限哦!</span>' <?=in_array($item['mid'],$group['rights'])?'checked':'' ?>> <hr> <?php if(!empty($item['children'])): ?> <div class="childMenu"> <?php foreach ($item['children'] as $key => $value): ?> <input type="checkbox" lay-skin="primary" name="rights[]" value="<?=$value['mid']?>" class="layui-input" title='<span style="color:#9e9e9e;" ><?=$value['title']?></span>' <?=in_array($value['mid'],$group['rights'])?'checked':'' ?>> <?php endforeach ?> </div> <?php endif ?> </div> <?php endforeach ?> </div> </div> </form> <div class="layui-input-block"> <button type="button" class="layui-btn" onclick="save()">保存</button> </div> </div></body></html><script type="text/javascript"> $ = layui.jquery; function save(){ // 利用form表单提交数据集 会自动将选中的菜单组成数组rights传递 /* $.post('/admin/group/add',$('form').serialize(),function(res){ if(res.code>0){ return layer.alert(res.msg,{icon:2}); } layer.alert(res.msg,{icon:1}); setTimeout(()=>{parent.window.location.reload();},1000); },'json');*/ // 手动获取需要提交的数据 var id = $('input[name="id"]').val(); var title = $('input[name="title"]').val(); var checks = $('form input[type="checkbox"]:checked'); // 循环取出每个复选框的菜单mid var rights = []; $.each(checks,(k,v)=>{ rights.push($(v).attr('value')); }); var data = { id:id, title:title, rights:rights }; $.post('/admin/group/edit',data,function(res){ if(res.code>0){ return layer.alert(res.msg,{icon:2}); } layer.alert(res.msg,{icon:1}); setTimeout(()=>{parent.window.location.reload();},1000); },'json'); }</script>
控制器修改角色和删除角色方法
// 修改角色 public function edit(){ if(Request::isPost()){ $data = Request::post(); if(empty(input('post.title'))){ exit(json_encode(array('code'=>1,'msg'=>'请填写角色名称'))); } if(empty(input('post.rights'))){ exit(json_encode(array('code'=>1,'msg'=>'请勾选权限菜单'))); } // 将数组中数据转换为整型 $data['rights'] = array_map('intval',$data['rights']); // 将勾选的权限数组转换为json字符串 $data['rights'] = json_encode($data['rights']); // 保存修改数据 $res = Db::table('admin_group')->save($data); if(!$res){ exit(json_encode(array('code'=>1,'msg'=>'修改失败'))); } exit(json_encode(array('code'=>0,'msg'=>'修改成功'))); }else{ $id = input('get.id'); // 该角色的权限列表mid $data['group'] = Db::table('admin_group')->where('id',$id)->find(); // 将取出的权限列表json字符串转换为数组 $data['group']['rights'] = json_decode($data['group']['rights']); // 权限菜单数据 $data['menus'] = $this->dataMenus(); return view('group/edit',$data); } }
删除就不做过多介绍了,点击删除按钮,将角色的id传入对应的控制器方法中,根据id删除对应记录即可
// 删除角色 public function del(){ $id = Request::post('id'); $res = DB::table('admin_group')->where('id',$id)->delete(); if(!$res){ exit(json_encode(array('code'=>1,'msg'=>'删除失败'))); } exit(json_encode(array('code'=>0,'msg'=>'删除成功'))); }
function del(id){ layer.confirm('确定要删除吗?',{ icon:3, btn: ['确定','取消'] },function(){ $.post('',{'id':id},function(res){ if(res.code>0){ layer.alert(res.msg,{icon:2}); }else{ layer.msg(res.msg); setTimeout(function(){window.location.reload();},1000); } },'json'); }); }
好了!到这里角色的增删改查以及权限分配就完成了。
1.【商城后台管理系统】基于TP6开发后台管理员登陆页面渲染部署
2.【商城后台管理系统】基于TP6开发后台管理界面渲染与无限级菜单查询
3.【商城后台管理系统】基于TP6开发登陆授权重定向拦截操作及管理员列表操作