基於EasyUI 快速搭建權限管理平臺

前言:javascript

一.用戶角色權限設計思路:

<1>不一樣職責的人員,對於系統操做的權限應該是不一樣;
<2>能夠對「組」進行權限分配;
<3>權限管理系統應該是可擴展的;
<4>知足業務系統中的功能權限php

2. 角色和操做的權限設計(
不一樣的應用場合,你可能會想出不一樣的需求,提了一個新的需求之後,可能會發現原來的設計沒方法實現了,因而還要添加一個新的表。這也是上面所提到的問題。 其實沒必要想得那麼複雜,權限能夠簡單描述爲:

某某主體 在 某某領域 有 某某權限

1,主體能夠是用戶,能夠是角色,也能夠是一個部門

2, 領域能夠是一個模塊,能夠是一個頁面,也能夠是頁面上的按鈕

3, 權限能夠是「可見」,能夠是「只讀」,也能夠是「可用」(如按鈕能夠點擊)

其實就是Who、What、How的問題, 誰對什麼功能,動做有怎樣的操做權限css

下面來看看錶結構是如何設計的,代碼以下:

from django.db import models

# Create your models here.


class Permission(models.Model):
    caption = models.CharField(max_length=32)
    parent_id = models.ForeignKey('Permission', related_name='k', to_field='id', null=True, blank=True)
    code = models.CharField(max_length=64, null=True,blank=True)
    method = models.CharField(max_length=16, null=True,blank=True)
    kwargs = models.CharField(max_length=128, null=True,blank=True)
    is_menu = models.BooleanField(default=False)

    def __str__(self):
        return self.caption

class Role(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class RoleToPermission(models.Model):
    menu_id = models.ForeignKey(Permission, to_field='id')
    role_id = models.ForeignKey(Role, to_field='id')

    def __str__(self):
        return "%s-%s" %(self.menu_id.caption, self.role_id.name)
# 目標,根據角色列表獲取權限 li
# 獲取當前用戶的全部標題權限
# RoleToPermission.objects.filter(role_id__in=li,menu_id__is_menu=True).\
#     values('menu_id__caption','menu_id__parent_id','menu_id__parent_id','menu_id__code')

# 獲取當前用戶的全部權限
# RoleToPermission.objects.filter(role_id__in=li).\
#     values('menu_id__caption','menu_id__parent_id','menu_id__parent_id','menu_id__code')

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

    def __str__(self):
        return self.username


class UserInfoToRole(models.Model):
    user_id = models.ForeignKey(UserInfo, to_field='id')
    role_id = models.ForeignKey(Role, to_field='id')
    def __str__(self):
        return '%s-%s' %(self.user_id.username, self.role_id.name)
用戶,角色, 權限表

 權限表直接將權限分爲具體的標題級別,url或者具體的某種操做方法html

##權限表與角色表關係前端

先來看看權限表的表結構是如何構造的:java

class Permission(models.Model):
    ##菜單的的權限,主要是爲後臺界面菜單部分單獨拆分出來的字段,跟下面定義的code,method,kwargs等字段進行區分
    caption = models.CharField(max_length=32)
    ##用於構造樹形結構的權限表,能夠本身關聯本身
    parent_id = models.ForeignKey('Permission', related_name='k', to_field='id', null=True, blank=True)
    ##具體的某個訪問的url
    code = models.CharField(max_length=64, null=True,blank=True)
    ##具體的請求方法
    method = models.CharField(max_length=16, null=True,blank=True)
    ##請求方法所帶的參數
    kwargs = models.CharField(max_length=128, null=True,blank=True)
    ##是不是菜單欄
    is_menu = models.BooleanField(default=False)

    def __str__(self):
        return self.caption

從上邊的表能夠看出,咱們將權限分類處理,這樣能夠更清晰準確的針對不一樣類型的權限進行劃分,而且構造出了有關聯關係的樹形結構node

接下來將權限表與角色表經過多對多關聯,代碼以下:jquery

class Role(models.Model):
    #定義角色名稱
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class RoleToPermission(models.Model):
    ##定義角色和權限的對應關係
    menu_id = models.ForeignKey(Permission, to_field='id')
    role_id = models.ForeignKey(Role, to_field='id')

    def __str__(self):
        return "%s-%s" %(self.menu_id.caption, self.role_id.name)
# 目標,根據角色列表獲取權限 li
# 獲取當前用戶的全部標題權限
# RoleToPermission.objects.filter(role_id__in=li,menu_id__is_menu=True).\
#     values('menu_id__caption','menu_id__parent_id','menu_id__parent_id','menu_id__code')

##用戶與角色關係django

用戶表UserInfo 經過中間表UserInfoToRole 多對多關聯 角色表Role,具體代碼以下session

class Role(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

    def __str__(self):
        return self.username


class UserInfoToRole(models.Model):
    user_id = models.ForeignKey(UserInfo, to_field='id')
    role_id = models.ForeignKey(Role, to_field='id')
    def __str__(self):
        return '%s-%s' %(self.user_id.username, self.role_id.name)

##登陸過程,根據用戶信息,關聯查詢獲取用戶相關角色,根據用戶所屬的角色關聯查詢用戶對應的權限,具體實現代碼以下:

def login(request):
    if request.method == 'POST':
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
        if obj:
            # 當前用戶信息保存至Session
            request.session['user_info'] = {'id': obj.id, 'name': obj.username}

            # 當前用戶角色列表保存至Session
            result_list = models.UserInfoToRole.objects.filter(user_id_id=obj.id).values('role_id_id')
            role_list = list(map(lambda x: x['role_id_id'], result_list))
            request.session['role_list'] = role_list

            # 當前用戶全部權限加入Session
            from django.db.models import Count, Min, Max, Sum
            permission_list = models.RoleToPermission.objects.filter(role_id__in=role_list).values(
                'menu_id_id').annotate(c=Count('menu_id_id')).values('menu_id__caption',
                                                                     'menu_id__parent_id',
                                                                     'menu_id__code',
                                                                     'menu_id__method',
                                                                     'menu_id__kwargs',
                                                                     'menu_id__id')
            # 根據permission_id去重
            permission_list = list(permission_list)
            request.session['permission_list'] = permission_list

            menu_list = models.RoleToPermission.objects.filter(role_id__in=role_list,menu_id__is_menu=True).values(
                'menu_id_id').annotate(c=Count('menu_id_id')).values('menu_id__caption',
                                                                     'menu_id__parent_id',
                                                                     'menu_id__code',
                                                                     'menu_id__method',
                                                                     'menu_id__kwargs',
                                                                     'menu_id__id',)
            # 根據permission_id去重
            menu_list = list(menu_list)
            request.session['menu_list'] = menu_list

            return redirect('/index/')
    return render(request, 'login.html')
Login

#自定義render,從session中獲取menu_list,代碼以下:

def my_render(request, template_name, context=None, *args, **kwargs):
    session_menu_list = request.session['menu_list']
    menu_list = build_tree(session_menu_list)
    print(request.path_info.split('/')[1])
    if context:
        context['menu_list'] = menu_list
    else:
        context = {'menu_list': menu_list}
    return render(request, template_name, context, *args, **kwargs)
my_render

#定義裝飾器validator,用來驗證用戶所請求的url是否已通過受權,代碼以下:

def validate(func):
    def inner(request,*args,**kwargs):
        session_perm_l = list(map(lambda  x : x['menu_id__code']  , request.session['permission_list']))
        if request.path_info.split('/')[1] in session_perm_l:
            ret = func(request,*args,**kwargs)
            return ret
        else:
            return HttpResponse('unauhoted')
    return inner
Validate

#經過遞歸方法構建權限樹形結構,代碼以下:

def build_node(menu_list, dic):
    #
    for menu in menu_list:
        if menu['id'] == dic['menu_id__parent_id']:
            temp = {'id': dic['menu_id__id'],'text': dic['menu_id__caption'], 'url': dic['menu_id__code'],'children': []}
            menu['children'].append(temp)
            break
        else:
            build_node(menu['children'], dic)

def build_tree(session_menu_list):
    # [ {menu_id__parent_id: None, 'menu_id__caption': '權限管理', 'menu_id__code': 'permission'},{},{} ]
    menu_list = []
    # menu_list = [{...}]
    for dic in session_menu_list:
        if dic['menu_id__parent_id'] == None:
            temp = {'id': dic['menu_id__id'],'text': dic['menu_id__caption'], 'url': dic['menu_id__code'],'children': []}
            menu_list.append(temp)
        else:
            # 當前
            build_node(menu_list, dic)
    return menu_list
build_tree

下面看下前端是如何實現的,前端使用EasyUI, 界面比較老舊,不過前端已經高度封裝,有利於提升開發效率,首先咱們先構建本身的母版,將基本的標題和樣式,js文件,並展現出左側菜單按鈕

具體實現方法以下:

首先EasyUI以west,east,north,south,center 將前端分文左右上下中間的佈局效果,具體實現代碼以下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Full Layout - jQuery EasyUI Demo</title>
    <link rel="stylesheet" type="text/css" href="/static/easyui/themes/default/easyui.css">
    <link rel="stylesheet" type="text/css" href="/static/easyui/themes/icon.css">
    <script type="text/javascript" src="/static/easyui/jquery.min.js"></script>
    <script type="text/javascript" src="/static/easyui/jquery.easyui.min.js"></script>
    <style>
        .crm-menu{
            display: block;
            padding: 8px;
            border-bottom: 1px dotted #dddddd;
        }
        .crm-menu:hover{
            background-color: #E0ECFF;
        }
    </style>
    {% block css %} {% endblock %}
</head>
<body class="easyui-layout">
    <div data-options="region:'north',border:false" style="height:60px;background:#B3DFDA;padding:10px">
        LOGO
    </div>
    <div data-options="region:'west',split:true,title:'West'" style="width:150px;">
        <div id="aa" class="easyui-accordion" style="width:100%;">

            {% for menu in menu_list %}
                <div title="{{ menu.text }}" data-options="iconCls:'icon-ok'" style="overflow:auto;">

                    {% for child in menu.children %}
                        <a id="menu_{{ child.url }}" href='/{{ child.url }}/' class='crm-menu'>{{ child.text }}</a>
                    {% endfor %}
                </div>
            {% endfor %}


        </div>
    
    </div>
    <div data-options="region:'south',border:false" style="height:50px;background:#A9FACD;padding:10px;">south region</div>
    <div data-options="region:'center',title:'Center'">
        {% block content %} {% endblock %}
    </div>

    {% block js %} {% endblock %}
</body>
</html>
_layout.html

母版定義好後,咱們能夠基於母版定義具備樹形結構的權限展現頁面.

EasyUI能夠經過兩種方式實現此效果,經過「easyui_tree」 類實現樹形菜單,也能夠經過js生成前端展現頁面,具體代碼以下:

{% extends 'layout/_layout.html' %}

{% block  content %}

    <div style="float: left;width: 300px">
        <ul id="pers_tree" ></ul>
        <!-- <ul id="tt" class="easyui-tree" data-options="url:'/get_permission_tree/',method:'get',animate:true"></ul> -->
    </div>

    <div style="float: left;width: 600px">
         <table id="dg"></table>
    </div>
    <div id="dlg" class="easyui-dialog" style="width:400px;height:200px;padding:10px 20px" closed="true" buttons="#dlg-buttons"> <!-#easyui訂製模態對話框,默認關閉狀態->
        <form id="fm1">
            <div class="input-group clearfix">
                <div class="group-label" style="width: 80px;">
                    <span>省份:</span>
                </div>
                <div class="group-input" style="width: 300px;">
                    <input id="dlg_nid" style="width: 200px;display: none"  name="nid"/>
                    <input id="dlg_province" style="width: 200px" class="easyui-textbox" type="text" name="caption" data-options="required:true,missingMessage:'省份不能爲空'" />
                </div>
            </div>
        </form>
    </div>
    <div id="dlg-buttons"> 
        <span id="dlg_summary" style="color: red"></span>
        <a href="#" class="easyui-linkbutton" iconCls="icon-ok" onclick="Save()">保存</a>
        <a href="#" class="easyui-linkbutton" iconCls="icon-cancel" onclick="javascript:$('#dlg').dialog('close')">取消</a>
    </div>
{% endblock %}

{% block js %}
    <script>
        $(function(){
            InitTree();
        });

        function InitTree(){
            $('#pers_tree').tree({
                url: '/get_permission_tree/',
                method: 'get',
                animate: true,
                onClick: function(node){
                    console.log(node.text,node.id);
                    InitTable(node.id);
                    InitPagination();
                }
            })
        }

        function InitTable(node_parent_id){
            $('#dg').datagrid({
                title: '聽不下去了',
                url: '/get_child_permission/',
                method: 'get',
                queryParams: {
                    node_parent_id: node_parent_id
                },
                columns: [[
                    {
                        field: 'ck',
                        checkbox: true
                    },
                    {
                        field: 'caption',
                        title: '標題',
                        width: 180,
                        align: 'center'
                    },
                    {
                        field: 'code',
                        title: 'URL',
                        width: 180,
                        align: 'center'
                    }

                ]],
                toolbar: [
                    {
                        text: '添加',
                        iconCls: 'icon-add',
                        handler: AddRow
                    }, {
                        text: '刪除',
                        iconCls: 'icon-remove',
                        handler: RemoveRow
                    }, {
                        text: '修改',
                        iconCls: 'icon-edit',
                        handler: EditRow
                    }
                ],
                pagePosition: 'both',
                pagination: true,
                pageSize: 10,
                pageNumber: 1,
                pageList: [10, 20, 50]
            })
        }

        function AddRow(){
            // 顯示對話框,因爲但願添加則將方法設置爲POST
            $('#fm1').form('clear');
            $('#dlg').dialog('open').dialog('setTitle','建立省份');
            $('#dlg_summary').empty();
            METHOD = 'post';  
        }
        function RemoveRow(){
            console.log('RemoveRow');
        }
        function EditRow(){
            console.log('EditRow');
        }

        function InitPagination() {
            var pager = $('#dg').datagrid('getPager');
            $(pager).pagination({
                beforePageText: '',
                afterPageText: '頁 共{pages}頁',
                displayMsg: '當前顯示{from}-{to}條記錄 共{total}條數據'
            })
        }
    </script>
{% endblock %}
View Code

 

EasyUI 已經高度封裝了樹形菜單的實現方法,咱們只要瞭解具體的參數便可,詳見http://www.jeasyui.com/demo/main/index.php

相關文章
相關標籤/搜索