批改状态:合格
老师批语:编程是有套路的, 有规律的
控制器部分的编码跟管理员管理很像, 有点机械的写完了.
西门老师说很难理解的递归实现, 自己感觉理解得还行, 就是很难用文字描述出来. 简单粗暴的解释, 就是方法自己调用自己, 但是方法处理的参数变了.
在全选/全消, 本级全选/全消的实现倒花了些时间. jQuery和js方面还得加强学习. 另外, 不需要钻牛角尖, 明明使用自定义属性pid就能轻松实现的功能, 偏偏要用上下级/兄弟选择器/过滤器来实现, 把自己绕晕了.
在参数验证方面, 有些许疑惑. 比如, 想验证管理员账号, 只能是以字母开头, 后续只能接字母, 数字和下划线, 似乎没有现成的方法可以使用, 变量过滤器好像也无法实现. 自己实现, 正则又不会, 且不同的验证, 要写很多验证规则, 也很繁琐. 请教老师, 有没有什么composer插件能简化参数验证的代码编写?
菜单管理基本可以参照管理员管理的编码思路来编码.
根据菜单上下级关系, 设置不同缩进量的伪树形下拉菜单项列表实现
/*** 根据菜单层级关系设置缩进量的伪树形结构下拉菜单项*/protected function getMenuTree($menus, $pid = 0, &$menuTree = [], $prefix = ' |-- '){// 遍历所有菜单项foreach ($menus as $menu) {// 获取当前父id下的所有子选项if ($menu['pid'] == $pid) {// 带有缩进量的菜单名$menu['show_title'] = $prefix . $menu['title'];// 加入到下拉菜单项数组$menuTree[] = $menu;// 下一级子选项, 增加6个空格缩进量$nextPrefix = str_repeat(' ', 6) . $prefix;// 递归获取当前菜单项的子菜单$this->getMenuTree($menus, $menu['mid'], $menuTree, $nextPrefix);}}return $menuTree;}
layui监听某个复选框选择事件的方法:
需监听的复选框添加 lay-filter="filter_tag" 属性, 给它添加一个区别于其他复选框的标签.
使用layui的form组件的on方法, 绑定具有标签名为 filter_tag 的复选框的选择事件及其处理脚本方法.
<!-- 其他布局元素... --><div class="layui-form-item"><label for="all_rights" class="layui-form-label"><input type="checkbox" name="all" id="all" class="layui-input" title="菜单全选" lay-filter="all" lay-skin="primary"></label></div><!-- 其他布局元素... --><!-- 绑定事件处理方法 --><script>layui.use(['layer', 'form'], function() {var layer = layui.layer;var form = layui.form;$ = layui.jquery;form.on('checkbox(sel_all)', function(data) {var val = data.elem.checked;// 一定要用prop, 用attr会有bug(jquery的bug)$(data.elem).parent().parent().find('input[name="rights[]"]').prop('checked', val);form.render('checkbox');})// 其他处理逻辑...}</script>
全选/全消当前菜单的所有子菜单
把当前菜单项的选择情况, 赋值给其子菜单项即可.
获取当前父菜单的所有子菜单的方法
pid 值为父id, 使用 $('input[type="checkbox"][pid="父id"]') 获取.prop('checked', true/false) 方法, 使用 attr('checked', true/false) 会出现只有前两次点击生效, 第三次点击开始无效的bug.视图文件代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>菜单列表</title><link rel="stylesheet" href="/static/plugin/layui/css/layui.css"><script src="/static/plugin/layui/layui.js"></script></head><body><div style="text-align: center; color: #666;"><h2>菜单列表</h2></div>@if(!empty($pid))<div style="float: left; height: 50px; line-height: 50px; padding: 0 10px;"><button class="layui-btn layui-btn-primary layui-btn-sm" onclick="returnBack({{$dad_menu['pid']}})">返回上一级</button></div>@endif<div style="float: right; height: 50px; line-height: 50px; padding: 0 10px;"><button class="layui-btn layui-btn-success layui-btn-sm" onclick="add()">新增</button></div><table class="layui-table"><thead><tr style="text-align: center"><th>排序</th><th>ID</th><th>图标</th><th>菜单名称</th><th>控制器</th><th>方法</th><th>是否隐藏</th><th>菜单状态</th><th>操作</th></tr></thead><tbody>@csrf@foreach($menus as $menu)<tr><td>{{$menu['ord']}}</td><td>{{$menu['mid']}}</td><td>{{$menu['icon']}}</td><td>{{$menu['title']}}</td><td>{{$menu['controller']}}</td><td>{{$menu['action']}}</td><td>{{$menu['ishidden'] ? '隐藏' : '显示'}}</td><td>{{$menu['status'] == 0 ? '正常' : '禁用'}}</td><td><button class="layui-btn layui-btn-primary layui-btn-xs" onclick="getSonMenu({{$menu['mid']}})">子菜单</button><button class="layui-btn layui-btn-xs" onclick="edit({{$menu['mid']}})">修 改</button>@if($menu['status'] == 0)<button class="layui-btn layui-btn-danger layui-btn-xs" onclick="delOrResume(1, {{$menu['mid']}}, '{{$menu['title']}}')">禁 用</button>@else<button class="layui-btn layui-btn-normal layui-btn-xs" onclick="delOrResume(0, {{$menu['mid']}}, '{{$menu['title']}}')">恢 复</button>@endif</td></tr>@endforeach</tbody></table></body><script>layui.use(['layer'], function() {layer = layui.layer;$ = layui.jquery;});function edit(id) {layer.open({type: 2,title: '修改管理员信息',shadeClose: false,// 应该是阴影区域的透明度shade: 0.8,// 分别是宽,高area: ['400px', '560px'],content: '/admin/menu/edit?mid=' + id,btn: ["提交"],yes: function(index, layero) {var body = layer.getChildFrame('body', index);// 得到iframe页的窗口对象var iframeWin = window[layero.find('iframe')[0]['name']];// 执行iframe页的方法: iframeWin.要调用的方法名();iframeWin.save();}});}function getSonMenu(pid) {window.location.href = "?pid=" + pid;}// 返回上一级: 参数是父菜单的父idfunction returnBack(ppid) {window.location.href="?pid=" + ppid;}function add() {layer.open({type: 2,title: '添加新菜单',// 点击弹出窗口外的阴影区域是否关闭弹出窗口shadeClose: false,shade: 0.8,area: ['400px', '560px'],content: '/admin/menu/add?pid={{empty($pid) ? 0 : $pid}}' //iframe的url, btn: ['保存']// 对应按钮区第一个按钮的执行脚本., yes: function(index, layero) {var body = layer.getChildFrame('body', index);// 得到iframe页的窗口对象var iframeWin = window[layero.find('iframe')[0]['name']];// 执行iframe页的方法: iframeWin.要调用的方法名();iframeWin.save();}});}function delOrResume(status, id, title) {var msg = status == 1 ? '禁用' : '恢复';//询问框layer.confirm('确定要' + msg + title + '吗?', {btn: ['确定','取消'] //按钮}, function(){var _token = $('input[name="_token"]').val();$.post('/admin/menu/del_resume', {id: id, resume: status, _token: _token}, function(res) {if(res.status != undefined && res.status == "0") {layer.msg(res.message, {icon: 1});setTimeout(() => {window.location.reload();}, 1000);} else if(res.status != undefined) {layer.alert(res.message, {icon: 2});} else {layer.alert('操作失败', {icon: 2});}// layer.alert(res);}, 'json');}, function() {});}</script></html>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>添加菜单</title><link rel="stylesheet" href="/static/plugin/layui/css/layui.css"><script src="/static/plugin/layui/layui.js"></script><style>body {padding: 10px;}.cms-label {text-align-last: justify;}</style></head><body><div class="layui-form">@csrf<div class="layui-form-item"><label for="pid" class="layui-form-label cms-label">上级菜单</label><div class="layui-input-inline"><select name="pid" id="pid" value="{{empty($pid) ? 0 : $pid}}"><option value="0" selected>一级菜单</option>@foreach($menuTree as $menu)<option value="{{$menu['mid']}}" {{$menu['mid'] == $pid ? 'selected' : ''}}>{!!$menu['show_title']!!}<option>@endforeach</select></div></div><div class="layui-form-item"><label for="title" class="layui-form-label cms-label">菜单名称</label><div class="layui-input-inline"><input type="text" class="layui-input" name="title" id="title"></div></div><div class="layui-form-item"><label for="controller" class="layui-form-label cms-label">控制器</label><div class="layui-input-inline"><input type="text" class="layui-input" name="controller" id="controller"></div></div><div class="layui-form-item"><label for="action" class="layui-form-label cms-label">方法名</label><div class="layui-input-inline"><input type="text" class="layui-input" name="action" id="action"></div></div><div class="layui-form-item"><label for="ishidden" class="layui-form-label cms-label">是否隐藏</label><div class="layui-inpu-inline"><input type="checkbox" name="ishidden" id="ishidden" title="隐藏"></div></div><div class="layui-form-item"><label for="status" class="layui-form-label cms-label">状态</label><div class="layui-inpu-inline"><input type="checkbox" name="status" id="status" title="禁用"></div></div><div class="layui-form-item"><label for="icon" class="layui-form-label cms-label">菜单图标</label><div class="layui-input-inline"><input type="text" class="layui-input" name="icon" id="icon"></div></div><div class="layui-form-item"><label for="ord" class="layui-form-label cms-label">菜单排序</label><div class="layui-input-inline"><input type="number" name="ord" id="ord" class="layui-input" value="0" min="0"></div></div><!-- <div class="layui-form-item"><div class="layui-input-block"><button type="button" class="layui-btn" onclick="save()">保存</button></div></div> --></div></body><script>layui.use(['layer', 'form'], function() {layer = layui.layer;form = layui.form;$ = layui.jquery;});function save() {// 数据var data = {_token: $('input[name="_token"]').val(),pid: $.trim($('select[name="pid"]').val()),title: $.trim($('input[name="title"]').val()),controller: $.trim($('input[name="controller"]').val()),action: $.trim($('#action').val()),icon: $.trim($('#icon').val()),status: $('#status').prop('checked') ? 1 : 0,ord: $('#ord').val(),ishidden: $('#ishidden').prop('checked') ? 1 : 0};// 判断数据有消息var res = dataCheck(data);if(res.status == 1) {return layer.alert(res.message, {icon: 2});}// 保存数据$.post('/admin/menu/save', data, function(res){if(res.status != undefined && res.status == '0') {layer.msg(res.message, {icon: 1});setTimeout(() => {window.parent.location.reload();}, 1000);} else if(res.status != undefined) {return layer.alert(res.message, {icon: 2});} else {return layer.alert('提交保存失败');}}, 'json');}function dataCheck(data) {var res = {status: 1};if(data.pid == undefined || data.pid == '') {res.message = '上级菜单id不能为空';return res;}if(data.title == undefined || data.title == '') {res.message = '菜单标题不能为空';return res;}if(data.controller == undefined || data.controller == '') {res.message = "控制器不能为空";return res;}if(data.ord == undefined || data.ord == '') {res.message = '排序不能为空';return res;}if(isNaN(data.ord)) {res.message = "排序必须为数字";return res;}return {status: 0};}</script></html>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>修改菜单</title><link rel="stylesheet" href="/static/plugin/layui/css/layui.css"><script src="/static/plugin/layui/layui.js"></script><style>body {padding: 10px;}.cms-label {text-align-last: justify;}</style></head><body><div class="layui-form">@csrf<input type="hidden" name="mid" id="mid" value="{{$menu['mid']}}"><div class="layui-form-item"><label for="pid" class="layui-form-label cms-label">上级菜单</label><div class="layui-input-inline"><select name="pid" id="pid" value="{{$menu['pid']}}"><option value="0" selected>一级菜单</option>@foreach($menuTree as $menuT)<option value="{{$menuT['mid']}}" {{$menu['pid'] == $menuT['mid'] ? 'selected' : ''}}>{!!$menuT['show_title']!!}<option>@endforeach</select></div></div><div class="layui-form-item"><label for="title" class="layui-form-label cms-label">菜单名称</label><div class="layui-input-inline"><input type="text" class="layui-input" name="title" id="title" value="{{$menu['title']}}"></div></div><div class="layui-form-item"><label for="controller" class="layui-form-label cms-label">控制器</label><div class="layui-input-inline"><input type="text" class="layui-input" name="controller" id="controller" value="{{$menu['controller']}}"></div></div><div class="layui-form-item"><label for="action" class="layui-form-label cms-label">方法名</label><div class="layui-input-inline"><input type="text" class="layui-input" name="action" id="action" value="{{$menu['action']}}"></div></div><div class="layui-form-item"><label for="ishidden" class="layui-form-label cms-label">是否隐藏</label><div class="layui-inpu-inline"><input type="checkbox" name="ishidden" id="ishidden" title="隐藏" {{$menu['ishidden'] ? 'checked' : ''}}></div></div><div class="layui-form-item"><label for="status" class="layui-form-label cms-label">是否禁用</label><div class="layui-inpu-inline"><input type="checkbox" name="status" id="status" title="禁用" {{$menu['status'] ? 'checked' : ''}}></div></div><div class="layui-form-item"><label for="icon" class="layui-form-label cms-label">菜单图标</label><div class="layui-input-inline"><input type="text" class="layui-input" name="icon" id="icon" value="{{$menu['icon']}}"></div></div><div class="layui-form-item"><label for="ord" class="layui-form-label cms-label">菜单排序</label><div class="layui-input-inline"><input type="number" name="ord" id="ord" class="layui-input" value="{{$menu['ord']}}" min="0"></div></div></div></body><script>layui.use(['layer', 'form'], function() {layer = layui.layer;form = layui.form;$ = layui.jquery;});function save() {// 数据var data = {_token: $('input[name="_token"]').val(),mid: $.trim($('input[name="mid"]').val()),pid: $.trim($('select[name="pid"]').val()),title: $.trim($('input[name="title"]').val()),controller: $.trim($('input[name="controller"]').val()),action: $.trim($('#action').val()),icon: $.trim($('#icon').val()),status: $('#status').prop('checked') ? 1 : 0,ord: $('#ord').val(),ishidden: $('#ishidden').prop('checked') ? 1 : 0};// 判断数据有消息var res = dataCheck(data);if(res.status == 1) {return layer.alert(res.message, {icon: 2});}// 保存数据$.post('/admin/menu/update', data, function(res){if(res.status != undefined && res.status == '0') {layer.msg(res.message, {icon: 1});setTimeout(() => {window.parent.location.reload();}, 1000);} else if(res.status != undefined) {return layer.alert(res.message, {icon: 2});} else {return layer.alert('提交保存失败');}}, 'json');}// 验证数据有效性function dataCheck(data) {var res = {status: 1};if(data.pid == undefined || data.pid == '') {res.message = '上级菜单id不能为空';return res;}if(data.title == undefined || data.title == '') {res.message = '菜单标题不能为空';return res;}if(data.controller == undefined || data.controller == '') {res.message = "控制器不能为空";return res;}if(data.ord == undefined || data.ord == '') {res.message = '排序不能为空';return res;}if(isNaN(data.ord)) {res.message = "排序必须为数字";return res;}return {status: 0};}</script></html>
<?phpnamespace App\Http\Controllers\admins;use App\Http\Controllers\Controller;use Illuminate\Http\Request;use Illuminate\Support\Facades\DB;use Illuminate\Support\Facades\Validator;class Menu extends Controller{/*** 当前层级菜单列表*/public function index(Request $req){// 获取父id, 如果没有传参, 则返回null, 强转null为整型, 则转为0.$pid = (int) $req->pid;// 入参的父id对应的0菜单项$data['menus'] = DB::table('admin_menu')->where('pid', $pid)->orderBy('controller')->orderBy('ord', 'desc')->lists();// 父菜单$data['dad_menu'] = DB::table('admin_menu')->where('mid', $pid)->getFirst();$data['pid'] = $pid;return view('/admins/menu/index', $data);}/*** 禁用/恢复菜单项*/public function delOrResume(Request $req){$mid = $req->id;$status = $req->resume;// 菜单id有效性判断if (!filter_var($mid, FILTER_VALIDATE_INT, ['option' => ['min_range' => 1]])) {return json_decode(['status' => 1, 'message' => '无效的菜单id']);};// 1=禁用; 0=启用if ($status)$status = 1;else$status = 0;$opera = $status ? '禁用' : '恢复';// 开启QueryLog// DB::connection()->enableQueryLog();// 执行$res = DB::table('admin_menu')->where('mid', $mid)->update(['status' => $status]);// 输出sql// dump(DB::getQueryLog());die;if ($res) {return json_encode(['status' => 0, 'message' => $opera . '成功!']);} else {return json_encode(['status' => 1, 'message' => $opera . '失败!']);}}/*** 添加菜单项页面*/public function add(Request $req){$menus = DB::table('admin_menu')->lists();// 获取根据层级关系进行缩进的菜单列表$data['menuTree'] = $this->getMenuTree($menus);$data['pid'] = $req->pid;return view('/admins/menu/add', $data);}/*** 新增保存*/public function save(Request $req){// 要保存的菜单数据$data = ['title' => trim($req->title),'controller' => trim($req->controller),'action' => trim($req->action),'icon' => trim($req->icon),'status' => intval(boolval(trim($req->status))),'ord' => intval(trim($req->ord)),'pid' => intval(trim($req->pid)),'ishidden' => intval(boolval(trim($req->ishidden))),'status' => intval(trim($req->status))];// 验证数据有效性$checked = $this->checkInput($data);if ($checked['status']) {return json_encode($checked);}// 返回插入的记录的id$res = DB::table('admin_menu')->insertGetId($data);// 开发人员默认拥有所有权限, 所以把新增的权限加入到开发人员的权限字段中$group = DB::table('admin_group')->where('gid', 1)->getFirst();$rights = json_decode($group['rights'], true);$rights[] = $res;$group['rights'] = '[' . implode(',', $rights) . ']';DB::table('admin_group')->where('gid', 1)->update($group);if ($res) {return json_encode(['status' => 0, 'message' => '创建成功']);}return json_encode(['status' => 1, 'message' => '创建失败']);}public function edit(Request $req){$mid = $req->mid;if (!is_numeric($mid) || $mid < 1) {return response('无效的菜单ID: ' . $mid, 200);}// 查询待编辑的菜单项$menu = DB::table('admin_menu')->where('mid', $mid)->getFirst();if (!$menu) {return response('菜单项不存在', 200);}$data['menu'] = $menu;// 获取有缩进量的菜单下拉列表项$menus = DB::table('admin_menu')->lists();$data['menuTree'] = $this->getMenuTree($menus);return view('/admins/menu/edit', $data);}public function update(Request $req){// 要保存的菜单数据$data = ['mid' => trim($req->mid),'title' => trim($req->title),'controller' => trim($req->controller),'action' => trim($req->action),'icon' => trim($req->icon),'status' => intval(boolval(trim($req->status))),'ord' => intval(trim($req->ord)),'pid' => intval(trim($req->pid)),'ishidden' => intval(boolval(trim($req->ishidden)))];// 验证数据有效性$checked = $this->checkInput($data, true);if ($checked['status']) {return json_encode($checked);}$res = DB::table('admin_menu')->where('mid', $data['mid'])->update($data);if ($res) {return json_encode(['status' => 0, 'message' => '修改成功']);}return json_encode(['status' => 1, 'message' => '修改失败']);}/*** 数据验证方法*/protected function checkInput($data, $checkId = false){$res = ['status' => 1];if($checkId) {if (empty($data['mid'])) {$res['message'] = '菜单主键不能为空';return $res;}}if (empty($data['title'])) {$res['message'] = '菜单标题不能为空';return $res;}if (empty($data['controller'])) {$res['message'] = '控制器名称不能为空';return $res;}return ['status' => 0];}/*** 根据菜单层级关系设置缩进量的伪树形结构下拉菜单项*/protected function getMenuTree($menus, $pid = 0, &$menuTree = [], $prefix = ' |-- '){// 遍历所有菜单项foreach ($menus as $menu) {// 获取当前父id下的所有子选项if ($menu['pid'] == $pid) {// 带有缩进量的菜单名$menu['show_title'] = $prefix . $menu['title'];// 加入到下拉菜单项数组$menuTree[] = $menu;// 下一级子选项, 增加6个空格缩进量$nextPrefix = str_repeat(' ', 6) . $prefix;// 递归获取当前菜单项的子菜单$this->getMenuTree($menus, $menu['mid'], $menuTree, $nextPrefix);}}return $menuTree;}}
视图文件代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>角色组列表</title><link rel="stylesheet" href="/static/plugin/layui/css/layui.css"><script src="/static/plugin/layui/layui.js"></script></head><body><div style="text-align: center; color: #666;"><h2>角色组列表</h2></div><div style="float: right; height: 50px; line-height: 50px; padding: 0 10px;"><button class="layui-btn layui-btn-success layui-btn-sm" onclick="add()">新增</button></div><table class="layui-table"><thead><tr><th>ID</th><th>角色组名称</th><th>授权菜单</th><th>操作</th></tr></thead><tbody>@foreach($groups as $group)<tr><td>{{$group['gid']}}</td><td style="min-width: 100px;">{{$group['title']}}</td><td>{{$group['right_title']}}</td><td style="min-width: 100px;"><span class="layui-btn layui-btn-success layui-btn-xs" onclick="setRights({{$group['gid']}})">授权</span><span class="layui-btn layui-btn-warm layui-btn-xs">停用</span></td></tr>@endforeach</tbody></table></body><script>layui.use(['layer', 'form'], function() {layer = layui.layer;form = layui.form;$ = layui.jquery;});function add() {layer.open({type: 2,title: '添加角色组',// 点击弹出窗口外的阴影区域是否关闭弹出窗口shadeClose: false,shade: 0.8,area: ['400px', '85%'],content: '/admin/groups/add' //iframe的url, btn: ['保存'], yes: function(index, layero) {var body = layer.getChildFrame('body', index);// 得到iframe页的窗口对象var iframeWin = window[layero.find('iframe')[0]['name']];// 执行iframe页的方法: iframeWin.要调用的方法名();iframeWin.save();}});}function setRights(gid) {layer.open({type: 2,title: '角色组授权',shadeClose: false,shade: 0.8,area: ['400px', '650px'],content: '/admin/groups/edit?gid=' + gid,btn: ['提交'],yes: function(index, layero) {var body = layer.getChildFrame('body', index);var iframeWin = window[layero.find('iframe')[0]['name']];iframeWin.save();}});}</script></html>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>角色组授权</title><link rel="stylesheet" href="/static/plugin/layui/css/layui.css"><script src="/static/plugin/layui/layui.js"></script><style>body {padding: 10px;}.sel_all{padding-top: 0px;}</style></head><body><div class="layui-form">@csrf<input type="hidden" name="gid" value="{{$group['gid']}}"><div class="layui-form-item"><label for="title" class="layui-form-label">角色组名</label><div class="layui-input-inline"><span class="layui-input">{{$group['title']}}</span></div></div><div class="layui-form-item"><label for="all_rights" class="layui-form-label"><input type="checkbox" name="all" id="all" class="layui-input" title="菜单全选" lay-filter="all" lay-skin="primary"></label></div>@foreach($menus as $menu)<div class="layui-form-item"><label for="" class="layui-form-label sel_all"><input type="checkbox" name="sel_all" class="layui-input" lay-skin="primary" title="本级全选" lay-filter="sel_all"></label>@if(!empty($menu['child_menus']))<div class="layui-input-inline"><input type="checkbox" {{in_array($menu['mid'], $group['rights']) ? 'checked' : ''}} name="rights[]" data-id="{{$menu['mid']}}" title="{{$menu['title']}}" class="layui-input" lay-skin="primary" lay-filter="sel_one"><br>@foreach($menu['child_menus'] as $cmenu)<input type="checkbox" {{in_array($cmenu['mid'], $group['rights']) ? 'checked' : ''}} name="rights[]" data-id="{{$cmenu['mid']}}" title="{{$cmenu['title']}}" class="layui-input" lay-skin="primary" lay-filter="sel_one">@endforeach</div>@endif</div>@endforeach</div></body><script>layui.use(['layer', 'form'], function() {var layer = layui.layer;var form = layui.form;$ = layui.jquery;form.on('checkbox(sel_all)', function(data) {var val = data.elem.checked;// 一定要用prop, 用attr会有bug(jquery的bug)$(data.elem).parent().parent().find('input[name="rights[]"]').prop('checked', val);form.render('checkbox');})// 子菜单项选装事件form.on('checkbox(sel_one)', function(data) {// 当前复选框的选中情况var val = data.elem.checked;// $(data.elem).attr('checked', val);// 获取所有选中的兄弟复选框var checked = $(data.elem).parent().find('input[type="checkbox"]:checked');// 获取所有兄弟复选框var allOne = $(data.elem).parent().find('input[type="checkbox"]');if(checked.length == allOne.length) {$(data.elem).parent().prev().children().filter('input').prop('checked', true);} else {$(data.elem).parent().prev().children().filter('input').prop('checked', false);}form.render('checkbox');});form.on('checkbox(all)', function(data) {var val = data.elem.checked;$('.layui-form-item:nth-child(n+2) input[type="checkbox"]').prop('checked', val);form.render('checkbox');});});function save() {var gid = $.trim($('input[name="gid"]').val());if(gid == "") {return layer.alert('用户组id出错', {icon: 2});}// 把选中的复选框对应的菜单id放入数组中var rights = [];$('input[name="rights[]"]:checked').each(function(index, checkbox) {rights.push($(checkbox).data('id'));});if(rights.length < 1) {return layer.alert('请选择权限', {icon: 2});}var _token = $('input[name="_token"]').val();var data = {gid: gid,_token: _token,// title: title,rights: rights};$.ajax({url: '/admin/groups/update',method: 'POST',data: data,dataType: 'json',success: function(res) {if(res.status != undefined && res.status == 0) {layer.msg(res.message, {icon: 1});setTimeout(() => {window.parent.location.reload();}, 1000);} else if(res.status != undefined) {return layer.alert(res.message, {icon: 2});} else {return layer.alert('保存失败', {icon: 2});}}});}</script></html>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>新增角色组</title><link rel="stylesheet" href="/static/plugin/layui/css/layui.css"><script src="/static/plugin/layui/layui.js"></script><style>body {padding: 10px;}.sel_all{padding-top: 0px;}</style></head><body><div class="layui-form">@csrf<div class="layui-form-item"><label for="title" class="layui-form-label">角色组名</label><div class="layui-input-inline"><input type="text" class="layui-input" name="title" id="title"></div></div><div class="layui-form-item"><label for="all_rights" class="layui-form-label"><input type="checkbox" name="all" id="all" class="layui-input" title="菜单全选" lay-filter="all" lay-skin="primary"></label></div>@foreach($menus as $menu)<div class="layui-form-item"><label for="" class="layui-form-label sel_all"><input type="checkbox" name="sel_all" class="layui-input" lay-skin="primary" title="本级全选" lay-filter="sel_all"></label>@if(!empty($menu['child_menus']))<div class="layui-input-inline"><input type="checkbox" name="rights[]" data-id="{{$menu['mid']}}" title="{{$menu['title']}}" class="layui-input" lay-skin="primary" lay-filter="sel_one"><br>@foreach($menu['child_menus'] as $cmenu)<input type="checkbox" name="rights[]" data-id="{{$cmenu['mid']}}" title="{{$cmenu['title']}}" class="layui-input" lay-skin="primary" lay-filter="sel_one">@endforeach</div>@endif</div>@endforeach</div></body><script>layui.use(['layer', 'form'], function() {var layer = layui.layer;var form = layui.form;$ = layui.jquery;// 本级所有子菜单全选/全消form.on('checkbox(sel_all)', function(data) {var val = data.elem.checked;// 一定要用prop, 用attr会有bug(jquery的bug)$(data.elem).parent().parent().find('input[name="rights[]"]').prop('checked', val);form.render('checkbox');})// 子菜单选中/取消选中form.on('checkbox(sel_one)', function(data) {var val = data.elem.checked;// $(data.elem).attr('checked', val);var notChecked = $(data.elem).parent().find('input[type="checkbox"]:checked');var allOne = $(data.elem).parent().find('input[type="checkbox"]');if(notChecked.length == allOne.length) {$(data.elem).parent().prev().children().filter('input').prop('checked', true);} else {$(data.elem).parent().prev().children().filter('input').prop('checked', false);}form.render('checkbox');});// 全选/全消所有菜单form.on('checkbox(all)', function(data) {var val = data.elem.checked;$('.layui-form-item:nth-child(n+2) input[type="checkbox"]').prop('checked', val);form.render('checkbox');});});function save() {// 用户组名称var title = $.trim($('input[name="title"]').val());if(title == undefined || title == "") {return layer.alert('请输入用户组名称', {icon: 2});}// 获得授权的权限id数组var rights = [];$('input[name="rights[]"]:checked').each(function(index, checkbox) {rights.push($(checkbox).data('id'));});if(rights.length < 1) {return layer.alert('请选择权限', {icon: 2});}// token不能漏var _token = $('input[name="_token"]').val();var data = {_token: _token,title: title,rights: rights};// 提交请求$.ajax({url: '/admin/groups/save',method: 'POST',data: data,dataType: 'json',success: function(res) {if(res.status != undefined && res.status == 0) {layer.msg(res.message, {icon: 1});setTimeout(() => {window.parent.location.reload();}, 1000);} else if(res.status != undefined) {return layer.alert(res.message, {icon: 2});} else {return layer.alert('保存失败', {icon: 2});}}});}</script></html>
服务端代码
<?phpnamespace App\Http\Controllers\admins;use App\Http\Controllers\Controller;use Illuminate\Support\Facades\DB;use Illuminate\Http\Request;class Groups extends Controller {public function index() {// 角色列表$groups = DB::table('admin_group')->lists();// 菜单列表$menus = DB::table('admin_menu')->where('status', 0)->orderBy('pid')->orderBy('mid')->keyval('mid', 'title');foreach($groups as &$group) {// 把json格式的字符串转为菜单id数组$rights = json_decode($group['rights'], true);// 拼接已授权的菜单字符串$rightTitles = '';foreach($rights as $right) {// 有效的权限id才拼接if(key_exists($right, $menus))$rightTitles .= ($menus[$right] . ',');}$group['right_title'] = substr($rightTitles, 0, -1);}return view('/admins/groups/index', ['groups' => $groups]);}public function add() {return view('/admins/groups/add', ['menus' => $this->getMenus()]);}function save(Request $req) {$title = $req->title;$rights = $req->rights;if(empty($title)) {return json_encode(['status' => 1, 'message' => '请录入用户组名称']);}if(!is_array($rights) || count($rights) < 1) {return json_encode(['status' => 1, `message` => '请选择权限']);}$data = ['title' => $title,// $req传过来的参数都是字符串/字符串数组, 所以不能用json_encode()方法转换.'rights' => '[' . implode(',', $rights) . ']'];$res = DB::table('admin_group')->insert($data);if($res) {return json_encode(['status' => 0, 'message' => '用户组添加成功']);} else {return json_encode(['status' => 1, `message` => '用户组添加失败']);}}function update(Request $req) {$gid = $req->gid;// $title = $req->title;$rights = $req->rights;if(empty($gid)) {return json_encode(['status' => 1, 'message' => '请录入用户组名称']);}if(!is_array($rights) || count($rights) < 1) {return json_encode(['status' => 1, `message` => '请选择权限']);}$data = [// 'title' => $title,'rights' => '[' . implode(',', $rights) . ']'];$res = DB::table('admin_group')->where('gid', $gid)->update($data);if($res) {return json_encode(['status' => 0, 'message' => '权限分配成功']);} else {return json_encode(['status' => 1, `message` => '权限分配失败']);}}function edit(Request $req) {// 要编辑的用户组id$gid = $req->gid;if(!is_numeric($gid) || $gid < 1) {return json_encode(['status' => 1, 'message' => '无效的用户组ID']);}// 查询要编辑的用户组数据$group = DB::table('admin_group')->where('gid', $gid)->getFirst();if(!$group) {return json_encode(['status' => 1, 'message' => '没有该用户组']);}// 该用户组已被授权的菜单id, 转为数组$group['rights'] = json_decode($group['rights'], true);$data['group'] = $group;// 有层级关系的菜单列表\$data['menus'] = $this->getMenus();return view('/admins/groups/edit', $data);}/*** 获取菜单列表*/protected function getMenus() {// 一级菜单$menu1sts = DB::table('admin_menu')->where('status', 0)->where('pid', 0)->keyval('mid');// 所有菜单$menus = DB::table('admin_menu')->where('status', 0)->keyval('mid');// 遍历所有菜单foreach($menus as $menu) {$pid = $menu['pid'];// 找到父级菜单if(key_exists($pid, $menu1sts)) {// 为父级菜单添加'child_menus'元素if(!isset($menu1sts[$pid]['child_menus'])) $menu1sts[$pid]['child_menus'] = [];$menu1sts[$pid]['child_menus'][] = $menu;}}return $menu1sts;}}
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号