菜單管理又稱爲資源管理,是系統資源對外的表現形式。本模塊主要是實現對菜單進行添加、修改、查詢、刪除等操做。其表設計語句以下:javascript
CREATE TABLE sys_menus
(css
id
int(11) NOT NULL AUTO_INCREMENT,html
name
varchar(50) DEFAULT NULL COMMENT '資源名稱',java
url
varchar(200) DEFAULT NULL COMMENT '資源URL',node
**type**
int(11) DEFAULT NULL COMMENT '類型 1:菜單 2:按鈕',jquery
sort
int(11) DEFAULT NULL COMMENT '排序',ajax
note
varchar(100) DEFAULT NULL COMMENT '備註',數據庫
parentId
int(11) DEFAULT NULL COMMENT '父菜單ID,一級菜單爲0',json
permission
varchar(500) DEFAULT NULL COMMENT '受權(如:sys:user:create)',bootstrap
createdTime
datetime DEFAULT NULL COMMENT '建立時間',
modifiedTime
datetime DEFAULT NULL COMMENT '修改時間',
createdUser
varchar(20) DEFAULT NULL COMMENT '建立用戶',
modifiedUser
varchar(20) DEFAULT NULL COMMENT '修改用戶',
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='資源管理';
菜單表與角色表是多對多的關係,在表設計時,多對多關係一般由中間表(關係表)進行維護,如圖-1所示:
圖-1
基於角色菜單表的設計,其角色和菜單對應的關係數據要存儲到關係表中,其具體存儲形式,如圖-2所示:
圖-2
菜單與角色的關係表腳本設計以下:
CREATE TABLE sys_role_menus
(
id
int(11) NOT NULL AUTO_INCREMENT,
role_id
int(11) DEFAULT NULL COMMENT '角色ID',
menu_id
int(11) DEFAULT NULL COMMENT 'ID',
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='角色與菜單對應關係';
基於用戶需求,實現菜單靜態頁面(html/css/js),經過靜態頁面爲用戶呈現菜單模塊的基本需求實現。
當在主頁左側菜單欄,點擊菜單管理時,在主頁內容呈現區,呈現菜單列表頁面,如
圖-3所示。
圖-3
當在菜單列表頁面點擊添加按鈕時,異步加載菜單編輯頁面,並在列表內容呈現區,呈現菜單編輯頁面,如圖-4所示。
圖-4
在菜單編輯頁面選擇上級菜單時,異步加載菜單信息,並以樹結構的形式呈現上級菜單,如圖-5所示。
圖-5
說明:假如客戶對此原型進行了確認,後續則能夠基於此原型進行研發。
菜單管理業務後臺API分層架構及調用關係如圖-6所示:
圖-6
說明:分層目的主要將複雜問題簡單化,實現各司其職,各盡所能。
菜單管理頁面的加載過程,其時序分析如圖-7所示:
圖-7
▪ 業務描述與設計實現
基於菜單管理的請求業務,在PageController中添加doMenuUI方法,用於返回菜單列表頁面。
▪ 關鍵代碼設計與實現
第一步:在PageController中定義返回菜單列表的方法。代碼以下:
@RequestMapping("menu/menu_list")
public String doMenuUI() {
return "sys/menu_list";
}
第二步:在PageController中基於rest風格的url方式優化返回UI頁面的方法。找出共性進行提取,例如:
@RequestMapping("{module}/{moduleUI}") public String doModuleUI(@PathVariable String moduleUI) { return "sys/"+moduleUI; }
▪ 業務描述與設計實現
首先準備菜單列表頁面(/templates/pages/sys/menu_list.html),而後在starter.html頁面中點擊菜單管理時異步加載菜單列表頁面。
▪ 關鍵代碼設計與實現
找到項目中的starter.html頁面,頁面加載完成之後,註冊菜單管理項的點擊事件,當點擊菜單管理時,執行事件處理函數。關鍵代碼以下:
$(function(){ … doLoadUI("load-menu-id","menu/menu_list") })
說明:對於doLoadUI函數,假如在starter.html中已經定義,則無需再次定義.
function doLoadUI(id,url){ $("#"+id).click(function(){ $("#mainContentId").load(url); }); }
其中,load函數爲jquery中的ajax異步請求函數。
▪ 業務描述與設計實現
本頁面呈現菜單信息時要以樹結構形式進行呈現。此樹結構會藉助jquery中的treeGrid插件進行實現,因此在菜單列表頁面須要引入treeGrid相關JS。可是,具體的treeGrid怎麼用可自行在網上進行查詢(已比較成熟)。
▪ 關鍵代碼設計與實現:
關鍵JS引入(menu_list.html),代碼以下:
<script type="text/javascript" src="bower_components/treegrid/jquery.treegrid.extension.js"></script> <script type="text/javascript" src="bowe r_components/treegrid/jquery.treegrid.min.js"></script> <script type="text/javascript" src="bower_components/treegrid/tree.table.js"></script>
菜單列表頁面加載完成,啓動菜單數據異步加載操做,本次菜單列表頁面要呈現菜單以及上級菜單信息,其數據查詢時,數據的封裝及傳遞過程,如圖-8所示。
圖-8
說明:本模塊將從數據庫查詢到的菜單數據封裝到map對象,一行記錄一個map對象,其中key爲表中的字段(列)名,值爲字段(列)對應的值。
數據加載過程其時序分析,如圖-9所示:
圖-9
▪ 業務描述及設計實現
經過數據層對象,基於業務層參數,查詢菜單以及上級菜單信息(要查詢上級菜單名)。
▪ 關鍵代碼分析及實現
第一步:定義數據層接口對象,經過此對象實現數據庫中菜單數據的訪問操做。關鍵代碼以下:
@Mapper public interface SysMenuDao { }
第二步:在SysMenuDao接口中添加findObjects方法,基於此方法實現菜單數據的查詢操做。代碼以下:
List<Map<String,Object>> findObjects();
說明:一行記錄映射爲一個map對象,多行存儲到list。
思考:這裏爲何使用map存儲數據,有什麼優點劣勢?
▪ 業務描述及設計實現
基於Dao接口建立映射文件,在此文件中經過相關元素(例如select)描述要執行的數據操做。
▪ 關鍵代碼設計及實現
第一步:在映射文件的設計目錄中添加SysMenuMapper.xml映射文件,代碼以下:
<?xml version=_"1.0"_ encoding=_"UTF-8"_?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace=_"com.cy.pj.sys.dao.SysMenuDao"_> </mapper>
第二步:在映射文件中添加id爲findObjects的元素,實現菜單記錄查詢。咱們要查詢全部菜單以及菜單對應的上級菜單名稱。關鍵代碼以下:
<select id=_"findObjects"_ resultType=_"map"_> <!-- 方案1 select c.*,p.name parentName from sys_menus c left join sys_menus p on c.parentId=p.id --> <!-- 方案2 --> select c.*,( select p.name from sys_menus p where c.parentId=p.id ) parentName from sys_menus c </select>
說明:自關聯查詢分析,如圖-10所示:
圖-10
▪ 業務描述與設計實現
在菜單查詢中,業務層對象主要是藉助數據層對象完成菜單數據的查詢。後續還能夠基於AOP對數據進行緩存,記錄訪問日誌等。
▪ 關鍵代碼設計及實現
第一步:定義菜單業務接口及方法,暴露外界對菜單業務數據的訪問,其代碼參考以下:
**package** com.cy.pj.sys.service; **public** **interface** SysMenuService { List<Map<String,Object>> findObjects(); }
第二步:定義菜單業務接口實現類,並添加菜單業務數據對應的查詢操做實現,其代碼參考以下:
package com.cy.pj.sys.service.impl; @Service public class SysMenuServiceImpl implements SysMenuService{ @Autowired private SysMenuDao sysMenuDao; @Override public List<Map<String, Object>> findObjects() { List<Map<String,Object>> list= sysMenuDao.findObjects(); if (list==null||list.size()==0) throw new ServiceException("沒有對應的菜單信息"); return list; }
▪ 業務描述與設計實現
控制層對象主要負責請求和響應數據的處理,例如,本模塊經過業務層對象執行業務邏輯,再經過VO對象封裝響應結果(主要對業務層數據添加狀態信息),最後將響應結果轉換爲JSON格式的字符串響應到客戶端。
▪ 關鍵代碼設計與實現
定義Controller類,並將此類對象使用Spring框架中的@Controller註解進行標識,表示此類對象要交給Spring管理。而後基於@RequestMapping註解爲此類定義根路徑映射。代碼參考以下:
package com.cy.pj.sys.controller; @RequestMapping("/menu/") @RestController public class SysMenuController { }
說明:這裏的@RestController註解等效於在類上同時添加了@Controller和 @ResponseBody註解.
在Controller類中添加菜單查詢處理方法,代碼參考以下:
@RequestMapping("doFindObjects") public JsonResult doFindObjects() { return new JsonResult(sysMenuService.findObjects()); }
▪ 業務描述與設計實現
菜單頁面加載完成之後,向服務端發起異步請求加載菜單信息,當菜單信息加載完成須要將菜單信息呈現到列表頁面上。
▪ 關鍵代碼設計與實現
第一步:在菜單列表頁面引入treeGrid插件相關的JS。
<script type="text/javascript" src="bower_components/treegrid/jquery.treegrid.extension.js"></script> <script type="text/javascript" src="bower_components/treegrid/jquery.treegrid.min.js"></script> <script type="text/javascript" src="bower_components/treegrid/tree.table.js"></script>
第二步:在菜單列表頁面,定義菜單列表配置信息,關鍵代碼以下:
var columns = [ { field : 'selectItem', radio : **true** }, { title : '菜單ID', field : 'id', align : 'center', valign : 'middle', width : '80px' }, { title : '菜單名稱', field : 'name', align : 'center', valign : 'middle', width : '130px' }, { title : '上級菜單', field : 'parentName', align : 'center', valign : 'middle', sortable : **true**, width : '100px' }, { title : '類型', field : 'type', align : 'center', valign : 'middle', width : '70px', formatter : **function**(item, index) { **if** (item.type == 1) { **return** '<span class="label label-success">菜單</span>'; } **if** (item.type == 2) { **return** '<span class="label label-warning">按鈕</span>'; } } }, { title : '排序號', field : 'sort', align : 'center', valign : 'middle', sortable : **true**, width : '70px' }, { title : '菜單URL', field : 'url', align : 'center', valign : 'middle', width : '160px' }, { title : '受權標識',//要顯示的標題名稱 field : 'permission',//json串中的key align : 'center',//水平居中 valign : 'middle',//垂直居中 sortable : **false** //是否排序 } ];//格式來自官方demos -->treeGrid(jquery擴展的一個網格樹插件)
第三步:定義異步請求處理函數,代碼參考以下:
function doGetObjects(){//treeGrid //1.構建table對象(bootstrap框架中treeGrid插件提供) var treeTable=new TreeTable( "menuTable",//tableId "menu/doFindObjects",//url columns); //設置從哪一列開始展開(默認是第一列) //treeTable.setExpandColumn(2); //2.初始化table對象(底層發送ajax請求獲取數據) treeTable.init();//getJSON,get(),... }
第四步:頁面加載完成,調用菜單查詢對應的異步請求處理函數,關鍵代碼以下:
$(function(){ doGetObjects(); })
基於用戶在列表頁面上選擇的的菜單記錄ID,執行刪除操做,本次刪除業務實現中,首先要基於id判斷當前菜單是否有子菜單,假若有子菜單則不容許刪除,沒有則先刪除菜單角色關係數據,而後再刪除菜單自身信息。其時序分析如圖-11所示:
如圖-11
▪ 業務描述及設計實現
數據層基於業務層提交的菜單記錄id,刪除菜單角色關係以及菜單數據,菜單自身記錄信息。
▪ 關鍵代碼設計及實現
第一步:建立SysRoleMenuDao並定義基於菜單id刪除關係數據的方法,關鍵代碼以下:
@Mapper public interface SysRoleMenuDao { int deleteObjectsByMenuId(Integer menuId); }
第二步:在SysMenuDao中添加基於菜單id查詢子菜單記錄的方法。代碼參考以下:
int getChildCount(Integer id);
第三步:在SysMenuDao中添加基於菜單id刪除菜單記錄的方法。代碼參考以下:
int deleteObject(Integer id);
▪ 業務描述及設計實現
在SysRoleMenuDao,SysMenuDao接口對應的映射文件中添加用於執行刪除業務的delete元素,而後在元素內部定義具體的SQL實現。
▪ 關鍵代碼設計與實現
第一步:建立SysRoleMenuMapper.xml文件並添加基於菜單id刪除關係數據的元素,關鍵代碼以下:
<?xml version=_"1.0"_ encoding=_"UTF-8"_?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace=_"com.cy.pj.sys.dao.SysRoleMenuDao"_> <delete id=_"deleteObjectsByMenuId"_ parameterType=_"int"_> delete from sys_role_menus where menu_id=#{menuId} </delete> </mapper>
第二步:在SysMenuMapper.xml文件中添加基於id統計子菜單數量的元素,關鍵代碼以下:
<select id="getChildCount" parameterType="int" resultType="int"> select count(*) from sys_menus where parentId=#{id} </select>
第三步:在SysMenuMapper.xml文件添加delete元素,基於帶單id刪除菜單自身記錄信息,關鍵代碼以下:
<delete id=_"deleteObject"_> delete from sys_menus where id =#{id} </delete>
▪ 業務描述與設計實現
在菜單業務層定義用於執行菜單刪除業務的方法,首先經過方法參數接收控制層傳遞的菜單id,並對參數id進行校驗。而後基於菜單id統計子菜單個數,假若有子菜單則拋出異常,提示不容許刪除。假如沒有子菜單,則先刪除角色菜單關係數據。最後刪除菜單自身記錄信息後並返回業務執行結果。
▪ 關鍵代碼設計與實現
第一步:在SysMenuService接口中,添加基於id進行菜單刪除的方法。關鍵代碼以下:
int deleteObject(Integer id);
第二步:在SysMenuServiceImpl實現類中注入SysRoleMenuDao相關對象。關鍵代碼以下:
@Autowired private SysRoleMenuDao sysRoleMenuDao;
第三步:在SysMenuServiceImpl實現類中添加刪除業務的具體實現。關鍵代碼以下:
@Override public int deleteObject(Integer id) { //1.驗證數據的合法性 if(id==null||id<=0) throw new IllegalArgumentException("請先選擇"); //2.基於id進行子元素查詢 int count=sysMenuDao.getChildCount(id); if(count>0) throw new ServiceException("請先刪除子菜單"); //3.刪除角色,菜單關係數據 sysRoleMenuDao.deleteObjectsByMenuId(id); //4.刪除菜單元素 int rows=sysMenuDao.deleteObject(id); if(rows==0) throw new ServiceException("此菜單可能已經不存在"); //5.返回結果 return rows; }
▪ 業務描述與設計實現
在菜單控制層對象中,添加用於處理菜單刪除請求的方法。首先在此方法中經過形參接收客戶端提交的數據,而後調用業務層對象執行刪除操做,最後封裝執行結果,並在運行時將響應對象轉換爲JSON格式的字符串,響應到客戶端。
▪ 關鍵代碼設計與實現
第一步:在SysMenuController中添加用於執行刪除業務的方法。代碼以下:
@RequestMapping("doDeleteObject")
public JsonResult doDeleteObject(Integer id){
sysMenuService.deleteObject(id);
return new JsonResult("delete ok");
}
第二步:啓動tomcat進行訪問測試,打開瀏覽器輸入以下網址:
http://localhost/menu/doDelet...
▪ 業務描述及設計實現
用戶在頁面上首先選擇要刪除的元素,而後點擊刪除按鈕,將用戶選擇的記錄id異步提交到服務端,最後在服務端執行菜單的刪除動做。
▪ 關鍵代碼設計與實現
第一步:頁面加載完成之後,在刪除按鈕上進行點擊事件註冊。關鍵代碼以下:
...
$(".input-group-btn")
.on("click",".btn-delete",doDeleteObject)
...
第二步:定義刪除操做對應的事件處理函數。關鍵代碼以下:
function doDeleteObject(){
//1.獲取選中的記錄id
var id=doGetCheckedId();
if(!id){
alert("請先選擇");
return;
}
//2.給出提示是否確認刪除
if(!confirm("確認刪除嗎"))return;
//3.異步提交請求刪除數據
var url="menu/doDeleteObject";
var params={"id":id};
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
$("tbody input[type='radio']:checked")
.parents("tr").remove();
}else{
alert(result.message);
}
});
}
第三步:定義獲取用戶選中的記錄id的函數。關鍵代碼以下:
function doGetCheckedId(){
//1.獲取選中的記錄
var selections=$("#menuTable")
//bootstrapTreeTable是treeGrid插件內部定義的jquery擴展函數
//getSelections爲擴展函數內部要調用的一個方法
.bootstrapTreeTable("getSelections");
//2.對記錄進行斷定
if(selections.length==1)
return selections[0].id;
}
添加頁面加載時序分析,如圖-12所示:
圖-12
首先準備菜單列表頁面(/templates/pages/sys/menu_edit.html),而後在menu_list.html頁面中點擊菜單添加時異步加載菜單編輯頁面。
▪ 業務描述與設計實現
菜單列表頁面點擊添加按鈕時,異步加載菜單編輯頁面。
▪ 關鍵代碼設計與實現
第一步:菜單列表頁面上,對添加按鈕進行事件註冊,關鍵代碼以下:
$(document).ready(function(){
...
$(".input-group-btn")
.on("click",".btn-add",doLoadEditUI);
});
第二步:定義添加按鈕事件處理函數,關鍵代碼以下:
function doLoadEditUI(){
var title;
if($(this).hasClass("btn-add")){
title="添加菜單"
}
var url="menu/menu_edit";
$("#mainContentId").load(url,function(){
$(".box-title").html(title);
})
}
在菜單編輯頁面上,點擊上級菜單時,其數據加載時序分析,如圖-13所示:
圖-13
▪ 業務描述與設計實現
定義值對象封裝查詢到的上級菜單id,name,parentId信息。
▪ 關鍵代碼設計與實現
package com.cy.pj.common.vo;
public class Node implements Serializable{
private static final long serialVersionUID = -6577397050669133046L;
private Integer id;
private String name;
private Integer parentId;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
@Override
public String toString() {
return "Node [id=" + id + ", name=" + name + ", parentId=" + parentId + "]";
}
}
▪ 業務描述與設計實現
基於請求獲取數據庫對應的菜單表中的全部菜單id,name,parentId,一行記錄封裝爲一個Node對象,多個node對象存儲到List集合
▪ 關鍵代碼設計與實現
在SysMenuDao接口中添加,用於查詢上級菜單相關信息。關鍵代碼以下:
List<Node> findZtreeMenuNodes();
▪ 業務描述與設計實現
基於SysMenuMapper中方法的定義,編寫用於菜單查詢的SQL元素。
▪ 關鍵代碼設計與實現
在SysMenuMapper.xml中添加findZtreeMenuNodes元素,用於查詢上級菜單信息。關鍵代碼以下:
<select id="findZtreeMenuNodes"
resultType="com.cy.pj.common.vo.Node">
select id,name,parentId
from sys_menus
</select>
▪ 業務描述與設計實現
基於用戶請求,經過數據層對象獲取上級菜單相關信息。
▪ 關鍵代碼實現
第一步:在SysMenuService接口中,添加查詢菜單信息的方法。關鍵代碼以下:
List<Node> findZtreeMenuNodes()
第二步:在SysMenuServiceImpl類中添加,查詢菜單信息方法的實現。關鍵代碼以下:
@Override
public List<Node> findZtreeMenuNodes() {
return sysMenuDao.findZtreeMenuNodes();
}
▪ 業務描述與設計實現
基於客戶端請求,訪問業務層對象方法,獲取菜單節點對象,並封裝返回。
▪ 關鍵代碼設計與實現
@RequestMapping("doFindZtreeMenuNodes")
public JsonResult doFindZtreeMenuNodes(){
return new JsonResult(
sysMenuService.findZtreeMenuNodes());
}
▪ 業務描述與設計實現
本模塊以開源JS組件方式實現ZTree結構信息的呈現。
▪ 關鍵代碼設計與實現
在menu_edit.html頁面中定義用於呈現樹結構的DIV組件(假如已有則無需定義)
<div class="layui-layer layui-layer-page layui-layer-molv layer-anim" id="menuLayer" type="page" times="2" showtime="0" contype="object"
style="z-index:59891016; width: 300px; height: 450px; top: 100px; left: 500px; display:none">
<div class="layui-layer-title" style="cursor: move;">選擇菜單</div>
<div class="layui-layer-content" style="height: 358px;">
<div style="padding: 10px;" class="layui-layer-wrap">
<ul id="menuTree" class="ztree"></ul> <!-- 動態加載樹 -->
</div>
</div>
<span class="layui-layer-setwin"> </span>
<div class="layui-layer-btn layui-layer-btn-">
</div>
</div>
▪ 業務描述與設計實現
引入zTree須要的JS,並,並基於JS中的定義的API初始化zTree中的菜單信息。
▪ 關鍵代碼設計與實現
第一步:引入js文件
<script type="text/javascript" src="bower_components/ztree/jquery.ztree.all.min.js"></script>
<script type="text/javascript" src="bower_components/layer/layer.js">
</script>
第二步:在menu_edit.html中定義zTree配置信息(初始化zTree時使用)
var zTree;
var setting = {
data : {
simpleData : {
enable : true,
idKey : "id", //節點數據中保存惟一標識的屬性名稱
pIdKey : "parentId", //節點數據中保存其父節點惟一標識的屬性名稱
rootPId : null //根節點id
}
}
}
第二步:定義異步加載zTree信息的函數,關鍵代碼以下:
function doLoadZtreeNodes(){
var url="menu/doFindZtreeMenuNodes";
//異步加載數據,並初始化數據
$.getJSON(url,function(result){
if(result.state==1){
//使用init函數須要先引入ztree對應的js文件
zTree=$.fn.zTree.init(
$("#menuTree"),
setting,
result.data);
$("#menuLayer").css("display","block");
}else{
alert(result.message);
}
})
}
第三步:定義zTree中取消按鈕事件處理函數,點擊取消隱藏zTree。關鍵代碼以下:
function doHideTree(){
$("#menuLayer").css("display","none");
}
第四步:定義zTree中肯定按鈕對應的事件處理處理函數。關鍵代碼以下:
function doSetSelectNode(){
//1.獲取選中的節點對象
var nodes=zTree.getSelectedNodes();
if(nodes.length==1){
var node=nodes[0];
console.log(node);
//2.將對象中內容,填充到表單
$("#parentId").data("parentId",node.id);
$("#parentId").val(node.name);
}
//3.隱藏樹對象
doHideTree();
}
第五步:定義頁面加載完成之後的事件處理函數:
$(document).ready(function(){
$("#mainContentId")
.on("click",".load-sys-menu",doLoadZtreeNodes)
$("#menuLayer")
.on("click",".btn-confirm",doSetSelectNode)
.on("click",".btn-cancel",doHideTree)
});
用戶在菜單編輯頁面輸入數據,而後異步提交到服務端,其簡易數據傳遞基本架構,如圖-14所示:
圖-14
用戶在菜單添加頁面中填寫好菜單數據,而後點擊保存按鈕,將用戶填寫的數據添加
到數據庫。其時序分析,如圖-15所示:
圖-15
▪ 業務描述與設計實現
定義持久化對象,封裝客戶端請求數據,並將數據傳遞到數據層進行持久化。
▪ 關鍵代碼設計與實現
菜單持久層對象類型定義,關鍵代碼以下:
public class SysMenu implements Serializable{
private static final long serialVersionUID = -8805983256624854549L;
private Integer id;
/*菜單名稱/
private String name;
/*菜單url: log/doFindPageObjects/
private String url;
/*菜單類型(兩種:按鈕,普通菜單)/
private Integer type=1;
/*排序(序號)/
private Integer sort;
/*備註/
private String note;
/*上級菜單id/
private Integer parentId;
/*菜單對應的權限標識(sys:log:delete)/
private String permission;
/*建立用戶/
private String createdUser;
/*修改用戶/
private String modifiedUser;
private Date createdTime;
private Date modifiedTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public Integer getSort() {
return sort;
}
public void setSort(Integer sort) {
this.sort = sort;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
}
▪ 業務描述與設計實現
負責將用戶提交的菜單數據,持久化到數據庫。
▪ 關鍵代碼設計與實現
在SysMenuDao接口中定義數據持久化方法:
int insertObject(SysMenu entity);
▪ 業務描述與設計實現
基於SysMenuDao中方法的定義,編寫用於實現菜單添加的SQL元素。
▪ 關鍵代碼設計與實現
在SysMenuMapper.xml中添加insertObject元素,用於寫入菜單信息。關鍵代碼以下:
<insert id=_"insertObject"_
parameterType=_"com.cy.pj.sys.entity.SysMenu"_>
insert into sys_menus
(name,url,type,sort,note,parentId,permission,
createdTime,modifiedTime,createdUser,modifiedUser)
values
(#{name},#{url},#{type},#{sort},#{note},#{parentId},
</insert>
▪ 業務描述與設計實現
基於控制層請求,調用數據層對象將菜單信息寫入到數據庫中。
▪ 關鍵代碼設計與實現
第一步:在SysMenuService接口中,添加用於保存菜單對象的方法。關鍵代碼以下:
int saveObject(SysMenu entity);
第二步:在SysMenuServiceImpl類中,實現菜單保存操做。關鍵代碼以下:
@Override
public int saveObject(SysMenu entity) {
//1.合法驗證
if(entity==null)
throw new IllegalArgumentException("保存對象不能爲空");
if(StringUtils._isEmpty_(entity.getName()))
throw new IllegalArgumentException("菜單名不能爲空");
//2.保存數據
int rows=sysMenuDao.insertObject(entity);
//3.返回數據
return rows;
}
▪ 業務描述與設計實現
接收客戶端提交的菜單數據,並對其進行封裝,而後調用業務層對象進行業務處理,最後將業務層處理結果響應到客戶端。
▪ 關鍵代碼設計與實現
定義Controller方法,藉助此方法處理保存菜單數據請求和響應邏輯。關鍵代碼以下:
@RequestMapping("doSaveObject")
public JsonResult doSaveObject(SysMenu entity){
sysMenuService.saveObject(entity);
return new JsonResult ("save ok");
}
▪ 業務描述與設計實現
點擊頁面cancel按鈕時,加載菜單那列表頁面。
▪ 關鍵代碼設計與實現
第一步:事件註冊(頁面加載完成之後)
$(".box-footer")
.on("click",".btn-cancel",doCancel)
第二步:事件處理函數定義
function doCancel(){
var url="menu/menu_list";
$("#mainContentId").load(url);
}
▪ 業務描述與設計實現
點擊頁面save按鈕時,將頁面上輸入的菜單信息異步提交到服務端。
▪ 關鍵代碼設計與實現
第一步:事件註冊(頁面加載完成之後)。
$(".box-footer")
.on("click",".btn-save",doSaveOrUpdate)
第二步:Save按鈕事件處理函數定義。關鍵代碼以下:
function doSaveOrUpdate(){
//1.獲取表單數據
var params=doGetEditFormData();
//2.定義url
var url="menu/doSaveObject";
//3.異步提交數據
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
});
}
第三步:表單數據獲取及封裝函數定義。關鍵代碼以下:
function doGetEditFormData(){
var params={
type:$("form input[name='typeId']:checked").val(),
name:$("#nameId").val(),
url:$("#urlId").val(),
sort:$("#sortId").val(),
permission:$("#permissionId").val(),
parentId:$("#parentId").data("parentId")
}
return params;
}
當在菜單列表頁面中選中某條記錄,而後點擊修改按鈕時,其業務時序分析如圖-16所示:
圖-16
▪ 業務描述與設計實現
點擊頁面修改按鈕時,獲取選中菜單記錄,並異步加載編輯頁面。
▪ 關鍵代碼設計與實現
第一步:列表頁面修改按鈕事件註冊,關鍵代碼以下:
$(".input-group-btn")
.on("click",".btn-update",doLoadEditUI);
第二步:修改按鈕事件處理函數定義或修改,關鍵代碼以下:
function doLoadEditUI(){
var title;
if($(this).hasClass("btn-add")){
title="添加菜單"
}else if($(this).hasClass("btn-update")){
title="修改菜單"
//獲取選中的記錄數據
var rowData=doGetCheckedItem();
if(!rowData){
alert("請選擇一個");
return;
}
$("#mainContentId").data("rowData",rowData);
}
var url="menu/menu_edit";
$("#mainContentId").load(url,function(){
$(".box-title").html(title);
})
}
第三步:獲取用戶選中記錄的函數定義。關鍵代碼以下:
function doGetCheckedItem(){
var tr=$("tbody input[type='radio']:checked")
.parents("tr");
return tr.data("rowData");
}
▪ 業務描述與設計實現
頁面加載完成,在頁面指定位置呈現要修改的數據。
▪ 關鍵代碼設計與實現
第一步:頁面加載完成之後,獲取頁面div中綁定的數據。關鍵代碼以下:
$(function(){
…
//假如是修改
var data=$("#mainContentId").data("rowData");
if(data)doInitEditFormData(data);
});
第二步:定義編輯頁面數據初始化方法。關鍵代碼以下:
function doInitEditFormData(data){
/* $("input[type='radio']").each(function(){
if($(this).val()==data.type){
$(this).prop("checked",true);
}
}) */
$(".typeRadio input[value='"+data.type+"']").prop("checked",true);
$("#nameId").val(data.name);
$("#sortId").val(data.sort);
$("#urlId").val(data.url);
$("#permissionId").val(data.permission);
$("#parentId").val(data.parentName);
$("#parentId").data("parentId",data.parentId);
}
當點擊編輯頁面更新按鈕時,其時序分析如圖-17所示:
圖-17
▪ 業務描述與設計實現
負責將用戶編輯頁面提交到服務端的菜單數據,更新到數據庫進行持久性存儲。
▪ 關鍵代碼設計與實現
在SysMenuDao接口中添加數據更新方法,關鍵代碼以下:
int updateObject(SysMenu entity);
▪ 業務描述與設計實現
基於SysMenuDao中updateObject方法的定義,編寫用於實現菜單更新的SQL元素。
▪ 關鍵代碼設計與實現
在SysMenuMapper.xml中添加updateObject元素,用於更新菜單信息。關鍵代碼以下:
<update id="updateObject"
parameterType="com.cy.pj.sys.entity.SysMenu">
update sys_menus
set
name=#{name},
type=#{type},
sort=#{sort},
url=#{url},
parentId=#{parentId},
permission=#{permission},
modifiedUser=#{modifiedUser},
modifiedTime=now()
where id=#{id}
</update>
▪ 業務描述與設計實現
基於控制層請求,對數據進行校驗並調用數據層對象將菜單信息更新到數據庫中。
▪ 關鍵代碼設計與實現
第一步:在SysMenuService接口中,添加用於更新菜單對象的方法。關鍵代碼以下:
int updateObject(SysMenu entity);
第二步:在SysMenuServiceImpl類中,實現菜單保存操做。關鍵代碼以下:
@Override
public int updateObject(SysMenu entity) {
//1.合法驗證
if(entity==null)
throw new ServiceException("保存對象不能爲空");
if(StringUtils._isEmpty_(entity.getName()))
throw new ServiceException("菜單名不能爲空");
//2.更新數據
int rows=sysMenuDao.updateObject(entity);
if(rows==0)
throw new ServiceException("記錄可能已經不存在");
//3.返回數據
return rows;
}
▪ 業務描述與設計實現
接收客戶端提交的菜單數據,並對其進行封裝,而後調用業務層對象進行業務處理,最後將業務層處理結果響應到客戶端。
▪ 關鍵代碼設計與實現
定義Controller方法,藉助此方法處理保存菜單數據請求和響應邏輯。關鍵代碼以下:
@RequestMapping("doUpdateObject")
public JsonResult doUpdateObject(
SysMenu entity){
sysMenuService.updateObject(entity);
return new JsonResult("update ok");
}
▪ 業務描述與設計實現
點擊頁面save按鈕時,將頁面上輸入的菜單編輯信息提交到服務端。
▪ 關鍵代碼設計與實現
編輯Save按鈕對應的事件處理函數。關鍵代碼以下:
function doSaveOrUpdate(){
//1.獲取表單數據
var params=doGetEditFormData();
var rowData=$("#mainContentId").data("rowData");
//2.定義url
var insertUrl="menu/doSaveObject";
var updateUrl="menu/doUpdateObject";
var url=rowData?updateUrl:insertUrl;
if(rowData)params.id=rowData.id;
//3.異步提交數據
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
});
}
▪ 菜單管理在整個系統中的定位(資源管理)。
▪ 菜單數據的自關聯查詢實現(查詢當前菜單以及這個菜單的上級菜單)。
▪ 菜單管理中數據的封裝過程(請求數據,響應數據)。
▪ 菜單數據在客戶端的呈現。(treeGrid,zTree)
▪ 菜單表是如何設計的,都有哪些字段?
▪ 菜單列表數據在客戶端是如何展現的?(TreeGrid)
▪ 菜單刪除業務是如何處理的?
▪ 菜單編輯頁面中上級菜單數據的呈現方式?(zTree)
▪ 經常使用錶鏈接方式,如圖-18所示:
圖-18
▪ 無效參數異常(IllegalArgumentException),,如圖-19所示:
圖-19
問題分析:檢查當前執行的業務,其結果映射配置,是否將resultType寫成了resultMap。
▪ 菜單編輯頁面,上級菜單樹結構呈現,如圖-20所示:
圖-20
問題分析:檢查查詢結果中是否有parentId,或映射對象Node中parentId是否寫錯。
▪ 屬性值注入失敗,如圖-21所示:
圖-21
問題分析:檢查Spring容器中是否有SysMenuService接口的實現類對象,由於在SysMenuController中須要一個這樣的對象。