動吧旅遊-菜單模塊設計

1 菜單管理設計說明

1.1 業務設計說明

菜單管理又稱爲資源管理,是系統資源對外的表現形式。本模塊主要是實現對菜單進行添加、修改、查詢、刪除等操做。其表設計語句以下: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所示:

image.png

圖-1

基於角色菜單表的設計,其角色和菜單對應的關係數據要存儲到關係表中,其具體存儲形式,如圖-2所示:

image.png

圖-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='角色與菜單對應關係';

1.2 原型設計說明

基於用戶需求,實現菜單靜態頁面(html/css/js),經過靜態頁面爲用戶呈現菜單模塊的基本需求實現。

當在主頁左側菜單欄,點擊菜單管理時,在主頁內容呈現區,呈現菜單列表頁面,如

圖-3所示。

image.png

圖-3

當在菜單列表頁面點擊添加按鈕時,異步加載菜單編輯頁面,並在列表內容呈現區,呈現菜單編輯頁面,如圖-4所示。

image.png

圖-4

在菜單編輯頁面選擇上級菜單時,異步加載菜單信息,並以樹結構的形式呈現上級菜單,如圖-5所示。

image.png

圖-5

說明:假如客戶對此原型進行了確認,後續則能夠基於此原型進行研發。

1.3 API設計說明

菜單管理業務後臺API分層架構及調用關係如圖-6所示:

image.png

圖-6

說明:分層目的主要將複雜問題簡單化,實現各司其職,各盡所能。

2 菜單管理列表頁面呈現

2.1 業務時序分析

菜單管理頁面的加載過程,其時序分析如圖-7所示:

image.png

圖-7

2.2 服務端實現

2.2.1 Controller實現

▪ 業務描述與設計實現

基於菜單管理的請求業務,在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;

}

2.3 客戶端實現

2.3.1 首頁菜單事件處理

▪ 業務描述與設計實現

首先準備菜單列表頁面(/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異步請求函數。

2.3.2 菜單列表頁面

▪ 業務描述與設計實現

本頁面呈現菜單信息時要以樹結構形式進行呈現。此樹結構會藉助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>

3 菜單管理列表數據呈現

3.1 數據架構分析

菜單列表頁面加載完成,啓動菜單數據異步加載操做,本次菜單列表頁面要呈現菜單以及上級菜單信息,其數據查詢時,數據的封裝及傳遞過程,如圖-8所示。

image.png

圖-8

說明:本模塊將從數據庫查詢到的菜單數據封裝到map對象,一行記錄一個map對象,其中key爲表中的字段(列)名,值爲字段(列)對應的值。

數據加載過程其時序分析,如圖-9所示:

image.png

圖-9

3.2 服務端關鍵業務及代碼實現

3.2.1 Dao接口實現

▪ 業務描述及設計實現

經過數據層對象,基於業務層參數,查詢菜單以及上級菜單信息(要查詢上級菜單名)。

▪ 關鍵代碼分析及實現

第一步:定義數據層接口對象,經過此對象實現數據庫中菜單數據的訪問操做。關鍵代碼以下:

@Mapper

public interface SysMenuDao {

}

第二步:在SysMenuDao接口中添加findObjects方法,基於此方法實現菜單數據的查詢操做。代碼以下:

List<Map<String,Object>> findObjects();

說明:一行記錄映射爲一個map對象,多行存儲到list。

思考:這裏爲何使用map存儲數據,有什麼優點劣勢?

3.2.2 Mapper文件實現

▪ 業務描述及設計實現

基於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所示:

image.png

圖-10

3.2.3 Service接口及實現類

▪ 業務描述與設計實現

在菜單查詢中,業務層對象主要是藉助數據層對象完成菜單數據的查詢。後續還能夠基於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;

}

3.2.4 Controller類實現

▪ 業務描述與設計實現

控制層對象主要負責請求和響應數據的處理,例如,本模塊經過業務層對象執行業務邏輯,再經過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());

}

3.3 客戶端關鍵業務及代碼實現

3.3.1 菜單列表信息呈現

▪ 業務描述與設計實現

菜單頁面加載完成之後,向服務端發起異步請求加載菜單信息,當菜單信息加載完成須要將菜單信息呈現到列表頁面上。

▪ 關鍵代碼設計與實現

第一步:在菜單列表頁面引入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();

})

4 菜單管理刪除操做實現

4.1 業務時序分析

基於用戶在列表頁面上選擇的的菜單記錄ID,執行刪除操做,本次刪除業務實現中,首先要基於id判斷當前菜單是否有子菜單,假若有子菜單則不容許刪除,沒有則先刪除菜單角色關係數據,而後再刪除菜單自身信息。其時序分析如圖-11所示:

image.png

如圖-11

4.2 服務端關鍵業務及代碼實現

4.2.1 Dao接口實現

▪ 業務描述及設計實現

數據層基於業務層提交的菜單記錄id,刪除菜單角色關係以及菜單數據,菜單自身記錄信息。

▪ 關鍵代碼設計及實現

第一步:建立SysRoleMenuDao並定義基於菜單id刪除關係數據的方法,關鍵代碼以下:

@Mapper

public interface SysRoleMenuDao {

int deleteObjectsByMenuId(Integer menuId);

}

第二步:在SysMenuDao中添加基於菜單id查詢子菜單記錄的方法。代碼參考以下:

int getChildCount(Integer id);

第三步:在SysMenuDao中添加基於菜單id刪除菜單記錄的方法。代碼參考以下:

int deleteObject(Integer id);

4.2.2 Mapper文件實現

▪ 業務描述及設計實現

在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>

4.2.3 Service接口及實現類

▪ 業務描述與設計實現

在菜單業務層定義用於執行菜單刪除業務的方法,首先經過方法參數接收控制層傳遞的菜單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;

}

4.2.4 Controller類實現

▪ 業務描述與設計實現

在菜單控制層對象中,添加用於處理菜單刪除請求的方法。首先在此方法中經過形參接收客戶端提交的數據,而後調用業務層對象執行刪除操做,最後封裝執行結果,並在運行時將響應對象轉換爲JSON格式的字符串,響應到客戶端。

▪ 關鍵代碼設計與實現

第一步:在SysMenuController中添加用於執行刪除業務的方法。代碼以下:

@RequestMapping("doDeleteObject")

public JsonResult doDeleteObject(Integer id){

sysMenuService.deleteObject(id);

return new JsonResult("delete ok");

}

第二步:啓動tomcat進行訪問測試,打開瀏覽器輸入以下網址:

http://localhost/menu/doDelet...

4.3 客戶端關鍵業務及代碼實現

4.3.1 菜單列表頁面事件處理

▪ 業務描述及設計實現

用戶在頁面上首先選擇要刪除的元素,而後點擊刪除按鈕,將用戶選擇的記錄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;

}

5 菜單添加頁面呈現

5.1 業務時序分析

添加頁面加載時序分析,如圖-12所示:

圖-12

5.2 準備菜單編輯頁面

首先準備菜單列表頁面(/templates/pages/sys/menu_edit.html),而後在menu_list.html頁面中點擊菜單添加時異步加載菜單編輯頁面。

5.3 菜單編輯頁面呈現

▪ 業務描述與設計實現

菜單列表頁面點擊添加按鈕時,異步加載菜單編輯頁面。

▪ 關鍵代碼設計與實現

第一步:菜單列表頁面上,對添加按鈕進行事件註冊,關鍵代碼以下:

$(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);

})

}

6 菜單編輯頁面上級菜單呈現

6.1 業務時序分析

在菜單編輯頁面上,點擊上級菜單時,其數據加載時序分析,如圖-13所示:

圖-13

6.2 服務端關鍵業務及代碼實現

6.2.1 Node對象

▪ 業務描述與設計實現

定義值對象封裝查詢到的上級菜單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 + "]";

}

}

6.2.2 Dao接口實現

▪ 業務描述與設計實現

基於請求獲取數據庫對應的菜單表中的全部菜單id,name,parentId,一行記錄封裝爲一個Node對象,多個node對象存儲到List集合

▪ 關鍵代碼設計與實現

在SysMenuDao接口中添加,用於查詢上級菜單相關信息。關鍵代碼以下:

List<Node> findZtreeMenuNodes();

6.2.3 Mapper映射文件

▪ 業務描述與設計實現

基於SysMenuMapper中方法的定義,編寫用於菜單查詢的SQL元素。

▪ 關鍵代碼設計與實現

在SysMenuMapper.xml中添加findZtreeMenuNodes元素,用於查詢上級菜單信息。關鍵代碼以下:

<select id="findZtreeMenuNodes"

resultType="com.cy.pj.common.vo.Node">

select id,name,parentId

from sys_menus

</select>

6.2.4 Service接口及實現類

▪ 業務描述與設計實現

基於用戶請求,經過數據層對象獲取上級菜單相關信息。

▪ 關鍵代碼實現

第一步:在SysMenuService接口中,添加查詢菜單信息的方法。關鍵代碼以下:

List<Node> findZtreeMenuNodes()

第二步:在SysMenuServiceImpl類中添加,查詢菜單信息方法的實現。關鍵代碼以下:

@Override

public List<Node> findZtreeMenuNodes() {

return sysMenuDao.findZtreeMenuNodes();

}

6.2.5 Controller類實現

▪ 業務描述與設計實現

基於客戶端請求,訪問業務層對象方法,獲取菜單節點對象,並封裝返回。

▪ 關鍵代碼設計與實現

@RequestMapping("doFindZtreeMenuNodes")

public JsonResult doFindZtreeMenuNodes(){

return new JsonResult(

sysMenuService.findZtreeMenuNodes());

}

6.3 客戶端關鍵業務及代碼實現

6.3.1 ZTree結構定義

▪ 業務描述與設計實現

本模塊以開源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>

6.3.2 ZTree數據呈現

▪ 業務描述與設計實現

引入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)

});

7 菜單數據添加實現

7.1 數據基本架構分析

用戶在菜單編輯頁面輸入數據,而後異步提交到服務端,其簡易數據傳遞基本架構,如圖-14所示:

圖-14

用戶在菜單添加頁面中填寫好菜單數據,而後點擊保存按鈕,將用戶填寫的數據添加

到數據庫。其時序分析,如圖-15所示:

圖-15

7.2 服務端關鍵業務及代碼實現

7.2.1 Entity類定義

▪ 業務描述與設計實現

定義持久化對象,封裝客戶端請求數據,並將數據傳遞到數據層進行持久化。

▪ 關鍵代碼設計與實現

菜單持久層對象類型定義,關鍵代碼以下:

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;

}

}

7.2.2 DAO接口定義

▪ 業務描述與設計實現

負責將用戶提交的菜單數據,持久化到數據庫。

▪ 關鍵代碼設計與實現

在SysMenuDao接口中定義數據持久化方法:

int insertObject(SysMenu entity);

7.2.3 Mapper映射文件定義

▪ 業務描述與設計實現

基於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},

{permission},now(),now(),#{createdUser},#{modifiedUser})

</insert>

7.2.4 Service接口定義及實現

▪ 業務描述與設計實現

基於控制層請求,調用數據層對象將菜單信息寫入到數據庫中。

▪ 關鍵代碼設計與實現

第一步:在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;

}

7.2.5 Controller類定義

▪ 業務描述與設計實現

接收客戶端提交的菜單數據,並對其進行封裝,而後調用業務層對象進行業務處理,最後將業務層處理結果響應到客戶端。

▪ 關鍵代碼設計與實現

定義Controller方法,藉助此方法處理保存菜單數據請求和響應邏輯。關鍵代碼以下:

@RequestMapping("doSaveObject")

public JsonResult doSaveObject(SysMenu entity){

sysMenuService.saveObject(entity);

return new JsonResult ("save ok");

}

7.3 客戶端關鍵業務及代碼實現

7.3.1 頁面cancel按鈕事件處理

▪ 業務描述與設計實現

點擊頁面cancel按鈕時,加載菜單那列表頁面。

▪ 關鍵代碼設計與實現

第一步:事件註冊(頁面加載完成之後)

$(".box-footer")

.on("click",".btn-cancel",doCancel)

第二步:事件處理函數定義

function doCancel(){

var url="menu/menu_list";

$("#mainContentId").load(url);

}

7.3.2 頁面Save按鈕事件處理

▪ 業務描述與設計實現

點擊頁面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;

}

8 菜單修改頁面數據呈現

8.1 業務時序分析

當在菜單列表頁面中選中某條記錄,而後點擊修改按鈕時,其業務時序分析如圖-16所示:

圖-16

8.2 客戶端關鍵業務及代碼實現

8.2.1 列表頁面修改按鈕事件處理

▪ 業務描述與設計實現

點擊頁面修改按鈕時,獲取選中菜單記錄,並異步加載編輯頁面。

▪ 關鍵代碼設計與實現

第一步:列表頁面修改按鈕事件註冊,關鍵代碼以下:

$(".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");

}

8.2.2 編輯頁面菜單數據呈現

▪ 業務描述與設計實現

頁面加載完成,在頁面指定位置呈現要修改的數據。

▪ 關鍵代碼設計與實現

第一步:頁面加載完成之後,獲取頁面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);

}

9 菜單數據更新實現

9.1 業務時序分析

當點擊編輯頁面更新按鈕時,其時序分析如圖-17所示:

圖-17

9.2 服務端關鍵業務及代碼實現

9.2.1 DAO接口實現

▪ 業務描述與設計實現

負責將用戶編輯頁面提交到服務端的菜單數據,更新到數據庫進行持久性存儲。

▪ 關鍵代碼設計與實現

在SysMenuDao接口中添加數據更新方法,關鍵代碼以下:

int updateObject(SysMenu entity);

9.2.2 Mapper映射文件定義

▪ 業務描述與設計實現

基於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>

9.2.3 Service接口及實現

▪ 業務描述與設計實現

基於控制層請求,對數據進行校驗並調用數據層對象將菜單信息更新到數據庫中。

▪ 關鍵代碼設計與實現

第一步:在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;

}

9.2.4 Controller類定義

▪ 業務描述與設計實現

接收客戶端提交的菜單數據,並對其進行封裝,而後調用業務層對象進行業務處理,最後將業務層處理結果響應到客戶端。

▪ 關鍵代碼設計與實現

定義Controller方法,藉助此方法處理保存菜單數據請求和響應邏輯。關鍵代碼以下:

@RequestMapping("doUpdateObject")

public JsonResult doUpdateObject(

SysMenu entity){

sysMenuService.updateObject(entity);

return new JsonResult("update ok");

}

9.3 客戶端關鍵業務及代碼實現

9.3.1 編輯頁面更新按鈕事件處理

▪ 業務描述與設計實現

點擊頁面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);

}

});

}

10 總結

10.1 重難點分析

▪ 菜單管理在整個系統中的定位(資源管理)。

▪ 菜單數據的自關聯查詢實現(查詢當前菜單以及這個菜單的上級菜單)。

▪ 菜單管理中數據的封裝過程(請求數據,響應數據)。

▪ 菜單數據在客戶端的呈現。(treeGrid,zTree)

10.2 FAQ分析

▪ 菜單表是如何設計的,都有哪些字段?

▪ 菜單列表數據在客戶端是如何展現的?(TreeGrid)

▪ 菜單刪除業務是如何處理的?

▪ 菜單編輯頁面中上級菜單數據的呈現方式?(zTree)

▪ 經常使用錶鏈接方式,如圖-18所示:

圖-18

10.3 BUG分析

▪ 無效參數異常(IllegalArgumentException),,如圖-19所示:

圖-19

問題分析:檢查當前執行的業務,其結果映射配置,是否將resultType寫成了resultMap。

▪ 菜單編輯頁面,上級菜單樹結構呈現,如圖-20所示:

圖-20

問題分析:檢查查詢結果中是否有parentId,或映射對象Node中parentId是否寫錯。

▪ 屬性值注入失敗,如圖-21所示:

圖-21

問題分析:檢查Spring容器中是否有SysMenuService接口的實現類對象,由於在SysMenuController中須要一個這樣的對象。

相關文章
相關標籤/搜索