對於後臺管理系統來講,要作好權限管理離不開菜單項和頁面按鈕控件功能的管理。因爲程序無法智能的知道有什麼菜單和控件,哪些人擁有哪些操做權限,因此首先要作的是菜單管理功能,將須要管理的菜單項和各個功能項添加(註冊)到菜單管理表中,方便後續權限控制管理。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"></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\"></i> 系統管理<i class=\"Hui-iconfont menu_dropdown-arrow\"></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/