個人第一個python web開發框架(36)——後臺菜單管理功能

  對於後臺管理系統來講,要作好權限管理離不開菜單項和頁面按鈕控件功能的管理。因爲程序無法智能的知道有什麼菜單和控件,哪些人擁有哪些操做權限,因此首先要作的是菜單管理功能,將須要管理的菜單項和各個功能項添加(註冊)到菜單管理表中,方便後續權限控制管理。javascript

  要開發一個菜單管理功能,離不開這些功能:菜單列表展現(須要菜單列表獲取接口)、新增菜單(新增接口)、編輯菜單(獲取菜單記錄以及提交修改接口)、刪除菜單(刪除接口),因爲菜單是多層級的關係,因此還須要增長菜單樹列表獲取接口來綁定菜單層級,在主頁面還須要增長菜單列表項輸出接口,用來展現菜單項。html

  在正式編寫菜單管理功能以前,咱們須要先在邏輯層(logic文件夾)中添加菜單邏輯類:menu_info_logic.py,繼承前面咱們開發的ORM基類,讓當前的菜單管理邏輯類擁有ORM的全部方法。前端

#!/usr/bin/env python
# coding=utf-8

from logic import _logic_base
from config import db_config


class MenuInfoLogic(_logic_base.LogicBase):
    """菜單管理表邏輯類"""

    def __init__(self):
        # 表名稱
        __table_name = 'menu_info'
        # 初始化
        _logic_base.LogicBase.__init__(self, db_config.DB, db_config.IS_OUTPUT_SQL, __table_name)

  爲了方便管理,咱們在api文件中建立system文件夾,用來存放全部後臺權限管理功能的代碼,並建立menu_info.py文件,來存放菜單管理接口java

  

  

  接下來咱們先實現菜單列表獲取接口,由第一部分的後端管理功能能夠知道,咱們前端使用的是jqGrid插件,這一塊咱們在前面已經實現過了,而ORM中也封裝好對應的方法,因此直接調用就能夠了。(這個接口在實現時,咱們要了解清楚的是,前端插件jqGrid它會傳遞什麼參數和須要返回什麼格式的數據回去)node

  jqGrid會經過接口,將當前頁面索引值page、頁面顯示記錄行數rows、排序字段sidx和排序方式sord(順序或倒序)提交到服務器端接口,若是咱們使用樹列表,它還會提交當前節點id參數nodeidpython

  因此咱們在服務器端接口須要作好這幾個參數的接收與使用操做,而後咱們經過調用前面實現的ORM的get_list方法,就能夠獲取對應的數據返回給客戶端了,具休代碼以下:nginx

 1 @get('/system/menu_info/')
 2 def callback():
 3     """
 4     獲取列表數據
 5     """
 6     # 菜單列表中,當前節點id,即父節點id
 7     parent_id = convert_helper.to_int0(web_helper.get_query('nodeid', '', is_check_null=False))
 8     # 頁面索引
 9     page_number = convert_helper.to_int1(web_helper.get_query('page', '', is_check_null=False))
10     # 頁面頁碼與顯示記錄數量
11     page_size = convert_helper.to_int0(web_helper.get_query('rows', '', is_check_null=False))
12     # 接收排序參數
13     sidx = web_helper.get_query('sidx', '', is_check_null=False)
14     sord = web_helper.get_query('sord', '', is_check_null=False)
15     # 初始化排序字段
16     order_by = 'sort asc'
17     if sidx:
18         order_by = sidx + ' ' + sord
19 
20     _menu_info_logic = menu_info_logic.MenuInfoLogic()
21     # 讀取記錄
22     wheres = 'parent_id=' + str(parent_id)
23     result = _menu_info_logic.get_list('*', wheres, page_number, page_size, order_by)
24     if result:
25         return json.dumps(result)
26     else:
27         return web_helper.return_msg(-1, "查詢失敗")

  7到18行,是接收參數。web

  20行初始化菜單邏輯類ajax

  22行是設置查詢條件,默認菜單列表咱們只顯示第一級菜單,也就是父id爲0的菜單。在列表第一次加載時,列表提交上來的nodeid爲空(即父節點爲默認爲0),因此設置查詢條件時父節點會賦值爲parent_id=0。當咱們點擊樹菜單展開時,才加載下一級菜單出來,這時jqGrid控件會再次訪問接口,提交當前要展開發節點id給接口,接口接收到參數之後返回對應的子節點列表給客戶端。sql

  get_list是前端ORM中封裝好的參數,它會返回jqGrid所須要的數據格式,因此第25行直接將符合jqGrid要求的數據返回給列表展現出來。

 

  咱們在後臺main.html中添加菜單,方便登陸後臺查看效果

  前端菜單管理的hmtl頁面你們自行下載源碼包查看,下面是完成後展現效果

  因爲當前尚未數據,因此暫時列表是空的,下面咱們建立添加和修改功能

 

  先看看新增頁面效果(頁面內容項通常咱們是根據數據字典和原型來設計的,你們能夠參照一下上一章菜單管理的數據結構)

  咱們須要接收頁面提交上來的這些參數,而後向數據庫中添加一條記錄

  上級菜單選項,這裏咱們點擊選擇時,須要顯示菜單樹列表,讓咱們選擇當前新增菜單項所屬菜單層級,方便菜單層級的管理,若是爲頂級菜單,則不須要進行選擇

  爲了讓後臺菜單好看一些,咱們能夠增長菜單小圖標,H-ui框架中,提供了字體圖標,這裏的查看增長連接到官網中,能夠直接查詢字體圖標編碼複製過來使用

  排序能夠輸入任意的數字,經過從 小到大順序排列菜單項,爲了方便排序項能夠自行累加,代碼中能夠獲取當前菜單層級最大值加1的方式來進行賦值

 1 @post('/api/system/menu_info/')
 2 def callback():
 3     """
 4     新增記錄
 5     """
 6     name = web_helper.get_form('name', '菜單名稱')
 7     icon = web_helper.get_form('icon', '菜單小圖標', True, 10, False, is_check_special_char=False)
 8     icon = icon.replace('\'', '').replace('|', '').replace('%', '')
 9     page_url = web_helper.get_form('page_url', '頁面URL', is_check_null=False)
10     interface_url = web_helper.get_form('interface_url', '接口url', is_check_null=False, is_check_special_char=False)
11     # 替換編碼
12     interface_url = interface_url.replace('@', '').replace('\'', '').replace('|', '').replace('%', '')
13     parent_id = convert_helper.to_int0(web_helper.get_form('parent_id', '父id', is_check_null=False))
14     sort = convert_helper.to_int0(web_helper.get_form('sort', '排序', is_check_null=False))
15     is_leaf = web_helper.get_form('is_leaf', '是否最終節點', is_check_null=False)
16     is_show = web_helper.get_form('is_show', '是否顯示', is_check_null=False)
17     is_enabled = web_helper.get_form('is_enabled', '是否啓用', is_check_null=False)
18 
19     _menu_info_logic = menu_info_logic.MenuInfoLogic()
20     # 計算深度級別,即當前菜單在哪一級
21     if parent_id == 0:
22         level = 0
23     else:
24         level = _menu_info_logic.get_value_for_cache(parent_id, 'level') + 1
25     # 若是沒有設置排序,則自動獲取當前級別最大的序號加1
26     if sort == 0:
27         sort = _menu_info_logic.get_max('parent_id', 'parent_id=' + str(parent_id)) + 1
28 
29     # 組合更新字段
30     fields = {
31         'name': string(name),
32         'icon': string(icon),
33         'page_url': string(page_url),
34         'interface_url': string(interface_url),
35         'parent_id': parent_id,
36         'sort': sort,
37         'level': level,
38         'is_leaf': is_leaf,
39         'is_show': is_show,
40         'is_enabled': is_enabled,
41     }
42     # 新增記錄
43     result = _menu_info_logic.add_model(fields)
44     if result:
45         return web_helper.return_msg(0, '提交成功')
46     else:
47         return web_helper.return_msg(-1, "提交失敗")

  前端菜單新增頁面(menu_info_edit.html)你們下載源碼包查看

 

  新增菜單頁面樹列表咱們使用的是zTree插件,它須要咱們輸出指定的數據格式才能正常顯示,因此調用接口返回:id、parent_id、name、open這幾個字段,在菜單項中,咱們有是否最終節點的字段,因此查詢條件中咱們指定查詢出全部非最終節點的項就能夠了

 1 @get('/api/system/menu_info/tree/')
 2 def callback():
 3     """
 4     獲取列表數據(樹列表)
 5     """
 6     _menu_info_logic = menu_info_logic.MenuInfoLogic()
 7     # 讀取記錄
 8     result = _menu_info_logic.get_list('id, parent_id, name, not is_leaf as open', 'is_leaf=false', orderby='sort asc')
 9     if result:
10         return web_helper.return_msg(0, "成功", {'tree_list': result.get('rows')})
11     else:
12         return web_helper.return_msg(-1, "查詢失敗")

  完成後直接填寫參數就能夠提交新增菜單記錄了。

  

  對於編輯接口,它基本上和新增接口代碼相差不大,區別地方有下面幾點:

  1.爲了減小菜單層級變動所形成的錯誤,在編輯記錄接口咱們須要屏蔽對父節點id的修改

  2.不須要再計算當前菜單所在層級的深度

  3.將新增方法add_model()更改成edit_model()方法

 1 @put('/api/system/menu_info/<id:int>/')
 2 def callback(id):
 3     """
 4     修改記錄
 5     """
 6     name = web_helper.get_form('name', '菜單名稱')
 7     icon = web_helper.get_form('icon', '菜單小圖標', True, 10, False, is_check_special_char=False)
 8     icon = icon.replace('\'', '').replace('|', '').replace('%', '')
 9     page_url = web_helper.get_form('page_url', '頁面URL', is_check_null=False)
10     interface_url = web_helper.get_form('interface_url', '接口url', is_check_null=False, is_check_special_char=False)
11     # 替換編碼
12     interface_url = interface_url.replace('\'', '').replace('|', '').replace('%', '')
13     parent_id = convert_helper.to_int0(web_helper.get_form('parent_id', '父id', is_check_null=False))
14     sort = convert_helper.to_int0(web_helper.get_form('sort', '排序', is_check_null=False))
15     is_leaf = web_helper.get_form('is_leaf', '是否最終節點', is_check_null=False)
16     is_show = web_helper.get_form('is_show', '是否顯示', is_check_null=False)
17     is_enabled = web_helper.get_form('is_enabled', '是否啓用', is_check_null=False)
18 
19     _menu_info_logic = menu_info_logic.MenuInfoLogic()
20     # 若是沒有設置排序,則自動獲取當前級別最大的序號加1
21     if sort == 0:
22         sort = _menu_info_logic.get_max('parent_id', 'parent_id=' + str(parent_id)) + 1
23 
24     # 組合更新字段
25     fields = {
26         'name': string(name),
27         'icon': string(icon),
28         'page_url': string(page_url),
29         'interface_url': string(interface_url),
30         'sort': sort,
31         'is_leaf': is_leaf,
32         'is_show': is_show,
33         'is_enabled': is_enabled,
34     }
35     # 修改記錄
36     result = _menu_info_logic.edit_model(id, fields)
37     if result:
38         return web_helper.return_msg(0, '提交成功')
39     else:
40         return web_helper.return_msg(-1, "提交失敗")

  你們能夠比較一下新增與編輯接口代碼,能夠發現代碼幾乎都是同樣的。

  

 

  最後增長刪除接口

 1 @delete('/api/system/menu_info/<id:int>/')
 2 def callback(id):
 3     """
 4     刪除指定記錄
 5     """
 6     _menu_info_logic = menu_info_logic.MenuInfoLogic()
 7     # 判斷要刪除的節點是否有子節點,是的話不能刪除
 8     if _menu_info_logic.exists('parent_id=' + str(id)):
 9         return web_helper.return_msg(-1, "當前菜單存在子菜單,不能直接刪除")
10 
11     # 刪除記錄
12     result = _menu_info_logic.delete_model(id)
13     if result:
14         return web_helper.return_msg(0, '刪除成功')
15     else:
16         return web_helper.return_msg(-1, "刪除失敗")

  刪除接口跟前端產品分類刪除接口同樣,在刪除前須要判斷當前菜單是否已被引用(即當前菜單下是否存在子菜單)

 

  菜單管理項添加完成後,列表效果圖

 

  完成這些以後,咱們還須要改造一下管理主界面左欄的菜單列表,改成從菜單管理數據表中讀取方式

  爲了方便後續權限的管理改造,咱們在接口中組合菜單代碼來實現菜單的展現效果。

  首先咱們經過查看左欄菜單列表的html代碼,提取出菜單html展現代碼

  而後在接口中,獲取設置爲顯示並啓用狀態的菜單列表

  經過循環判斷,拼接一級菜單和二級菜單項的html輸出代碼

  最後將結果輸出到前端展現出來

 1 @get('/api/main/menu_info/')
 2 def callback():
 3     """
 4     主頁面獲取菜單列表數據
 5     """
 6     _menu_info_logic = menu_info_logic.MenuInfoLogic()
 7     # 讀取記錄
 8     result = _menu_info_logic.get_list('*', 'is_show and is_enabled', orderby='sort')
 9     if result:
10         # 定義最終輸出的html存儲變量
11         html = ''
12         for model in result.get('rows'):
13             # 提取出第一級菜單
14             if model.get('parent_id') == 0:
15                 # 添加一級菜單
16                 temp = """
17                 <dl id="menu-%(id)s">
18                     <dt><i class="Hui-iconfont">%(icon)s</i> %(name)s<i class="Hui-iconfont menu_dropdown-arrow">&#xe6d5;</i></dt>
19                     <dd>
20                         <ul>
21                 """ % {'id': model.get('id'), 'icon': model.get('icon'), 'name': model.get('name')}
22                 html = html + temp
23 
24                 # 從全部菜單記錄中提取當前一級菜單下的子菜單
25                 for sub_model in result.get('rows'):
26                     # 若是父id等於當前一級菜單id,則爲當前菜單的子菜單
27                     if sub_model.get('parent_id') == model.get('id'):
28                         temp = """
29                         <li><a data-href="%(page_url)s" data-title="%(name)s" href="javascript:void(0)">%(name)s</a></li>
30                     """ % {'page_url': sub_model.get('page_url'), 'name': sub_model.get('name')}
31                         html = html + temp
32 
33                 # 閉合菜單html
34                 temp = """
35                         </ul>
36                     </dd>
37                 </dl>
38                     """
39                 html = html + temp
40 
41         return web_helper.return_msg(0, '成功', {'menu_html': html})
42     else:
43         return web_helper.return_msg(-1, "查詢失敗")

  執行後會輸出下面結果:

{
    "data": {
        "menu_html": "\n                <dl id=\"menu-1\">\n                    <dt><i class=\"Hui-iconfont\">&#xe62e;</i> 系統管理<i class=\"Hui-iconfont menu_dropdown-arrow\">&#xe6d5;</i></dt>\n                    <dd>\n                        <ul>\n                \n                        <li><a data-href=\"menu_info.html\" data-title=\"菜單管理\" href=\"javascript:void(0)\">菜單管理</a></li>\n                    \n                        </ul>\n                    </dd>\n                </dl>\n                    "
    },
    "msg": "成功",
    "state": 0
}

  前端經過AJAX獲取菜單列表hmtl代碼,而後添加到後臺左欄菜單列表中就實現咱們想要的效果了

<aside class="Hui-aside">
    <div class="menu_dropdown bk_2" id="menu">

    </div>
</aside>


<script type="text/javascript">
    $(function () {
        $.ajax({
            url: "/api/main/menu_info/?" + 100 * Math.random(),
            type: "GET",
            dataType:'json',
            success: function (data) {
                if (checkLogin(data, true))
                {
                    $("#menu").html(data.data.menu_html);
                    $.Huifold(".menu_dropdown dl dt",".menu_dropdown dl dd","fast",1,"click");
                }
            }
        });
    });
</script>

  頁面展現效果

  對於菜單管理的改造,完成上面這些項就算完厲了。對於菜單權限的控制,後續完成整個改造後會專門講解。

 

  權限系統中的部門管理(角色權限組管理),它的基本功能和菜單功能類似,因此就不開新章節進行講解,你們能夠根據數據結構嘗試編寫,也能夠參考本節提供的源碼進行研究。

  PS:部門管理中,部門編碼生成是一個比較特殊的方法,須要多debug理解。

 

 

  本文對應的源碼下載 (內附本章源碼對應數據庫表單和記錄建立sql代碼,上傳的圖片若是顯示不了,能夠nginx.conf配置的location項中添加upload,第一部分章節的nginx那裏忘記添加了)

 

版權聲明:本文原創發表於 博客園,做者爲 AllEmpty 本文歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然視爲侵權。

python開發QQ羣:669058475(本羣已滿)、733466321(能夠加2羣)    做者博客:http://www.cnblogs.com/EmptyFS/

相關文章
相關標籤/搜索