记一次权限管理(RBAC)

简易版

1 用户表 角色表 权限表

 

2.管理员表

CREATE TABLE `administrator` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `isactive` varchar(255) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

3.角色表

CREATE TABLE `roles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '角色名称',
  `description` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `isactive` char(1) DEFAULT NULL,
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

 

4.用户角色表(多对多)

CREATE TABLE `administrator_roles` (
  `administrator_id` int(11) NOT NULL,
  `roles_id` int(11) NOT NULL,
  PRIMARY KEY (`administrator_id`,`roles_id`),
  UNIQUE KEY `administrator_id` (`administrator_id`,`roles_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

5.权限表(最重要)

CREATE TABLE `permissions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '权限名称,例如 user.create',
  `display_name` varchar(255) NOT NULL COMMENT '权限显示名称',
  `description` varchar(255) DEFAULT NULL COMMENT '权限描述',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `route` varchar(255) DEFAULT NULL,
  `http_method` varchar(255) DEFAULT NULL COMMENT 'GET/POST/PUT/DELETE',
  `pid` int(11) DEFAULT NULL,
  `grade` int(11) DEFAULT NULL,
  `is_menu` char(1) DEFAULT NULL,
  `sort` int(11) DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='权限表';

6.角色权限表 (多对多)

CREATE TABLE `role_permission` (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`),
  KEY `permission_id` (`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

7.权限管理的实现

用户登录的时候校验过用户之后查询权限


$roles = $Row->administratorRoles()->select();
$allPermissions = [];

foreach ($roles as $role) {
    foreach ($role->RolesPermission()->select() as $permission) {
        $allPermissions[$permission->id] = $permission->toArray(); 
    }
}

// 去重后的权限列表
$permissionArray = array_values($allPermissions);

// 提取非空 route
$routePermissions = array_column(array_filter($permissionArray, function($item) {
    return !empty($item['route']);
}), 'route');

// 存入 Session
Session::set('permissions', $routePermissions);
Session::set('permission_all', $permissionArray);
$permissionArray 用于左边栏展示
//前端展示
<ul id="nav"> <?php foreach($menus as $menu): ?> <li> <a href="javascript:;"> <i class="iconfont left-nav-li" lay-tips="<?=$menu['description']?>">&#xe6b8;</i> <cite><?=$menu['description']?></cite> <i class="iconfont nav_right">&#xe697;</i> </a> <?php if (!empty($menu['children'])): ?> <ul class="sub-menu"> <?php foreach($menu['children'] as $sub): ?> <li> <a onclick="xadmin.add_tab('<?= $sub['description'] ?>','<?= $sub['route'] ?>')"> <i class="iconfont">&#xe6a7;</i> <cite><?= $sub['description'] ?></cite></a> </li> <?php endforeach; ?> </ul> <?php endif; ?> </li> <?php endforeach; ?> </ul>
//php 提供数据
public function index()
    {
        $permission=Session::get('permission_all',[]);
        if(is_array($permission)){
            $menus=array_filter($permission, function($p){
                return isset($p['is_menu']) && $p['is_menu'] == 'Y';
            });
            // 构造树形结构
            $treeMenus = $this->buildMenuTree($menus);
            return view('index', ['menus' => $treeMenus]);
        }else{
            return view('index');
        }

    }


    /**
     * Left 目录 构造树
     * */
    public function buildMenuTree($items, $parentId= 0){
        $items = array_values($items);
        $tree = [];
        foreach ($items as $item) {
            if ($item['pid'] == $parentId) {
                $children = $this->buildMenuTree($items, $item['id']);
                if ($children) {
                    $item['children'] = $children;
                }
                $tree[] = $item;
            }
        }
        return $tree;
    }

 

$routePermissions 用于用户组权限的编辑
//前端展示

<body>
<div class="layui-fluid">
    <div class="layui-row">
        <form action="" method="post" class="layui-form layui-form-pane">
            <div class="layui-form-item">
                <label class="layui-form-label" style="font-size: 16px;color: #333;font-weight: bold;">权限管理</label>
            </div>

            <div class="permission-container">
                <!-- 系统管理模块 -->
                <?php foreach ($permissionTree as $permission): ?>
                    <div class="permission-module">
                        <div class="module-header">
                            <div class="header-content">
                                <input type="checkbox" name="Permission[<?= $permission['id'] ?>]" value="<?= $permission['id'] ?>" lay-skin="primary" lay-filter="father" data-level="1" lay-ignore>
                                <span class="header-title"><?= $permission['description'] ?? '' ?></span>
                                <i class="layui-icon layui-icon-down"></i>
                            </div>
                        </div>

                        <?php if (!empty($permission['children'])): ?>
                            <div class="module-content">
                                <?php foreach ($permission['children'] as $child): ?>
                                    <div class="permission-group">
                                        <div class="group-header">
                                            <div class="header-content">
                                                <input type="checkbox"  lay-skin="primary" lay-filter="father" data-level="2" lay-ignore>
                                                <span class="header-title"><?= $child['description'] ?? '' ?></span>
                                                <i class="layui-icon layui-icon-down"></i>
                                            </div>
                                        </div>
                                        <div class="group-items">
                                            <input name="Permission[<?= $child['id'] ?>]"
                                                   lay-skin="primary"
                                                   type="checkbox"
                                                   data-level="3"
                                                   title="<?= $child['display_name'] ?? '' ?>"
                                                   value="<?= $child['id'] ?>"
                                                <?= in_array($child['id'], $checkedIds) ? 'checked' : '' ?>
                                            >
                                            <?php if (!empty($child['children'])): ?>
                                                <?php foreach ($child['children'] as $value): ?>
                                                    <input name="Permission[<?= $value['id'] ?>]"
                                                           lay-skin="primary"
                                                           type="checkbox"
                                                           data-level="3"
                                                           title="<?= $value['display_name'] ?? '' ?>"
                                                           value="<?= $value['id'] ?>"
                                                        <?= in_array($value['id'], $checkedIds) ? 'checked' : '' ?>
                                                    >
                                                <?php endforeach; ?>
                                            <?php endif; ?>
                                        </div>
                                    </div>
                                <?php endforeach; ?>
                            </div>
                        <?php endif; ?>
                    </div>
                <?php endforeach; ?>
                <input type="hidden" name="roleId" value="<?= $roleId ?>">
            </div>

            <div class="layui-form-item" style="margin-top: 20px;text-align: center;">
                <button class="layui-btn layui-btn-lg layui-btn-normal" style="width: 120px;" lay-submit=""
                        lay-filter="add">保存设置
                </button>
            </div>
        </form>
    </div>
</div>

<style>

    .permission-container {
        background: #fff;
        border-radius: 5px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
        padding: 15px;
    }

    .permission-module {
        margin-bottom: 15px;
        border: 1px solid #e6e6e6;
        border-radius: 4px;
        overflow: hidden;
    }

    .module-header {
        background: #f8f8f8;
        padding: 12px 15px;
        border-bottom: 1px solid #e6e6e6;
    }

    .header-content {
        display: flex;
        align-items: center;
        cursor: pointer;
    }

    .header-content .layui-form-checkbox {
        margin-right: 10px;
    }

    .header-title {
        flex: 1;
    }

    .header-content .layui-icon {
        font-size: 14px;
        color: #999;
        transition: transform 0.3s;
    }

    .module-content {
        padding: 10px 15px;
        background: #fff;
    }

    .permission-group {
        margin-bottom: 10px;
        border: 1px solid #f0f0f0;
        border-radius: 3px;
    }

    .group-header {
        padding: 8px 15px;
        background: #fbfbfb;
    }

    .group-items {
        padding: 10px 15px 10px 40px;
        display: flex;
        flex-wrap: wrap;
        gap: 15px;
    }

    .permission-module.active .module-header .layui-icon,
    .permission-group.active .group-header .layui-icon {
        transform: rotate(180deg);
    }


</style>

<script>
    layui.use(['form', 'layer', 'jquery'], function () {
        var $ = layui.jquery;
        var form = layui.form;
        var layer = layui.layer;

        // 初始化折叠状态
        $('.module-content').hide();
        $('.group-items').hide();

        // 模块折叠/展开 - 点击标题区域时触发
        $('.module-header .header-content').on('click', function (e) {
            // 如果点击的是复选框,则不处理
            if ($(e.target).is('input[type="checkbox"]') || $(e.target).closest('.layui-form-checkbox').length) {
                return;
            }

            var module = $(this).closest('.permission-module');
            module.toggleClass('active');
            module.find('.module-content').slideToggle(200);
            $(this).find('.layui-icon').toggleClass('layui-icon-down layui-icon-up');
        });

        // 分组折叠/展开 - 点击标题区域时触发
        $('.group-header .header-content').on('click', function (e) {
            // 如果点击的是复选框,则不处理
            if ($(e.target).is('input[type="checkbox"]') || $(e.target).closest('.layui-form-checkbox').length) {
                return;
            }

            var group = $(this).closest('.permission-group');
            group.toggleClass('active');
            group.find('.group-items').slideToggle(200);
            $(this).find('.layui-icon').toggleClass('layui-icon-down layui-icon-up');
        });


        $(document).ready(function () {
            // 遍历所有 group(data-level=2 的父容器)
            $('.permission-group').each(function () {
                updateGroupStatus($(this));
            });

            // 遍历所有 module(data-level=1 的父容器)
            $('.permission-module').each(function () {
                // 传一个该模块下任意一个三级的元素进去用于查找最近父级
                var $anyCheckbox = $(this).find('input[data-level=3]').first();
                if ($anyCheckbox.length) {
                    updateParentModuleStatus($anyCheckbox);
                }
            });

            // 可选:刷新 Layui checkbox 样式(不加 lay-ignore 的会变)
            form.render();
        });


        // 监听一级(data-level=1)checkbox
        $('input[data-level=1]').on('change', function () {
            var checked = this.checked;
            $(this).closest('.permission-module')
                .find('input[type="checkbox"]')
                .prop('checked', checked)
                .prop('indeterminate', false);

            form.render(); // 刷新 Layui 样式(只对没加 lay-ignore 的有效)
        });

        // 监听二级(data-level=2)checkbox
        $('input[data-level=2]').on('change', function () {
            var checked = this.checked;
            var $group = $(this).closest('.permission-group');

            $group.find('.group-items input[type="checkbox"]')
                .prop('checked', checked);

            // 更新一级模块状态
            updateParentModuleStatus($(this));

            form.render();
        });

        // 子级 checkbox 变动
        form.on('checkbox', function (data) {
            if (!data.elem.hasAttribute('lay-filter')) {
                var $group = $(data.elem).closest('.permission-group');
                var $module = $(data.elem).closest('.permission-module');

                // 更新组头(level 2)状态
                updateGroupStatus($group);

                // 更新模块头(level 1)状态
                updateParentModuleStatus($(data.elem));
                form.render();
            }
        });


        // 更新组头状态(全选、半选、全不选)
        function updateGroupStatus($group) {

            var $checkboxes = $group.find('.group-items input[type="checkbox"]');
            var $groupHeader = $group.find('input[data-level=2]')[0];
            var total = $checkboxes.length;

            var checked = $checkboxes.filter(':checked').length;
            if (checked === 0) {
                $groupHeader.checked = false;
                $groupHeader.indeterminate = false;
            } else if (checked === total) {
                $groupHeader.checked = true;
                $groupHeader.indeterminate = false;
            } else {
                $groupHeader.checked = false;
                $groupHeader.indeterminate = true;
            }
        }

        // 更新模块头状态(全选、半选、全不选)
        function updateParentModuleStatus($elem) {
            var $module = $elem.closest('.permission-module');
            var $moduleHeader = $module.find('input[data-level=1]')[0];

            var $allLevel3 = $module.find('input[data-level=3]');
            var total = $allLevel3.length;
            var checked = $allLevel3.filter(':checked').length;

            if (checked === 0) {
                $moduleHeader.checked = false;
                $moduleHeader.indeterminate = false;
            } else if (checked === total) {
                $moduleHeader.checked = true;
                $moduleHeader.indeterminate = false;
            } else {
                $moduleHeader.checked = false;
                $moduleHeader.indeterminate = true;
            }
        }


        //监听提交
        form.on('submit(add)', function (data) {
            //因为使用了lay-ignore layui的部分功能被禁用需要手动找补
            $('input[data-level=1]').each(function () {
                const $this = $(this);
                if (this.indeterminate) {
                    this.checked = true;
                }
                if (this.checked) {
                    data.field[$this.attr('name')] = $this.val();
                }
            });


            $.post('/role/permissionsEdit',data.field,function (res){
                if(res.code == 200){
                    layer.msg(res.msg, {icon: 1});
                }else{
                    layer.msg(res.msg, {icon: 2});
                }
            },'json')
            return false;
        });
    });
</script>
</body>
</html>
//php提供数据
public function RolesPermission(Request $request)
    {
        $roleId = request()->get('id', null);
        if ($roleId) {
            //用户组所有用的权限
            $ownedPermissionIds = Db::name('role_permission')
                ->where('role_id', $roleId)
                ->column('permission_id');
            $allPermissions = permissions::order('sort')->select()->toArray();
            //所有权限目录展示
            $permissionTree = $this->buildPermissionTree($allPermissions);
            return view('roles/Permission', [
                'permissionTree' => $permissionTree,
                'checkedIds' => $ownedPermissionIds,
                'roleId' => $roleId,
            ]);
        }

    }

    function buildPermissionTree(array $permissions)
    {
        $map = [];
        $tree = [];

        // 初始化所有节点,添加 children 数组
        foreach ($permissions as $item) {
            $item['children'] = [];
            $map[$item['id']] = $item;
        }

        // 遍历所有节点,按 pid 挂到父节点的 children 上
        foreach ($map as $id => &$node) {
            if ($node['pid'] == 0) {
                // 根节点
                $tree[] = &$node;
            } else {
                // 子节点,挂到父节点上
                if (isset($map[$node['pid']])) {
                    $map[$node['pid']]['children'][] = &$node;
                }
            }
        }
        return $tree;
    }     

 

 更新权限

public function RolePermissionEdit(Request $request)
    {
        $roleId = request()->post('roleId', null);
        $Permissions = request()->post('Permission', null);
        $PermissionId = array_values($Permissions);
        if ($roleId) {
            try {
                Db::transaction(function () use ($roleId, $PermissionId) {
                    Db::name('role_permission')->where('role_id', $roleId)->delete();

                    $insertData = [];
                    foreach ($PermissionId as $pid) {
                        $insertData[] = [
                            'role_id' => $roleId,
                            'permission_id' => $pid,
                        ];
                    }
                    if (!empty($insertData)) {
                        Db::name('role_permission')->insertAll($insertData);
                    }
                });
                return json([
                    'code' => 200,
                    'msg' => '权限修改成功'
                ]);
            } catch (\Exception $e) {
                return json([
                    'code' => 400,
                    'msg' => '权限修改失败:' . $e->getMessage()
                ]);
            }
        } else {
            return json([
                'code' => 400,
                'msg' => '用户组不存在'
            ]);
        }
    }

 

posted @ 2025-07-02 16:11  SHACK元  阅读(26)  评论(0)    收藏  举报