共计 20006 个字符,预计需要花费 51 分钟才能阅读完成。
-
业务设计说明
菜单治理又称为资源管理,是系统资源对外的表现形式。本模块次要是实现对菜单进行增加、批改、查问、删除等操作。其表设计语句如下:
CREATE TABLE `sys_menus` (`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL COMMENT '资源名称',
`url` varchar(200) DEFAULT NULL COMMENT '资源 URL',
`type` int(11) DEFAULT NULL COMMENT '类型 1:菜单 2:按钮',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
`parentId` int(11) DEFAULT NULL COMMENT '父菜单 ID,一级菜单为 0',
`permission` varchar(500) DEFAULT NULL COMMENT '受权(如:sys:user:create)',
`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 所示:
基于角色菜单表的设计,其角色和菜单对应的关系数据要存储到关系表中,其具体存储模式,如图 - 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 所示。
当在菜单列表页面点击增加按钮时,异步加载菜单编辑页面,并在列表内容出现区,出现菜单编辑页面, 如图 - 4 所示。
在菜单编辑页面抉择上级菜单时,异步加载菜单信息,并以树结构的模式出现上级菜单,如图 - 5 所示。
阐明: 如果客户对此原型进行了确认, 后续则能够基于此原型进行研发。
-
API 设计说明
菜单治理业务后盾 API 分层架构及调用关系如图 - 6 所示:
阐明:分层目标次要将简单问题简单化,实现各司其职,各尽所能。
-
菜单治理列表页面出现
-
业务时序剖析
菜单治理页面的加载过程,其时序剖析如图 - 7 所示:
-
服务端实现
-
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;}
-
客户端实现
-
首页菜单事件处理
- 业务形容与设计实现
首先筹备菜单列表页面(/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 所示。
阐明:本模块将从数据库查问到的菜单数据封装到 map 对象,一行记录一个 map 对象,其中 key 为表中的字段 (列) 名,值为字段 (列) 对应的值。数据加载过程其时序剖析,如图 - 9 所示: -
服务端要害业务及代码实现
-
Dao 接口实现
- 业务形容及设计实现
通过数据层对象,基于业务层参数,查问菜单以及上级菜单信息(要查问上级菜单名)。
- 要害代码剖析及实现
第一步:定义数据层接口对象,通过此对象实现数据库中菜单数据的拜访操作。要害代码如下:
@Mapper
public interface SysMenuDao {}
第二步:在 SysMenuDao 接口中增加 findObjects 办法,基于此办法实现菜单数据的查问操作。代码如下:
List<Map<String,Object>> findObjects();
阐明:一行记录映射为一个 map 对象, 多行存储到 list。
-
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 所示:
-
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;
}
-
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());
}
-
客户端要害业务及代码实现
-
菜单列表信息出现
- 业务形容与设计实现
菜单页面加载实现当前,向服务端发动异步申请加载菜单信息,当菜单信息加载实现须要将菜单信息出现到列表页面上。 - 要害代码设计与实现
第一步:在菜单列表页面引入 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 所示:
-
服务端要害业务及代码实现
-
-
Dao 接口实现
- 业务形容及设计实现
数据层基于业务层提交的菜单记录 id,删除菜单角色关系以及菜单数据,菜单本身记录信息。
- 要害代码设计及实现
第一步:创立 SysRoleMenuDao 并定义基于菜单 id 删除关系数据的办法,要害代码如下:
@Mapper
public interface SysRoleMenuDao {int deleteObjectsByMenuId(Integer menuId);
}
第二步:在 SysMenuDao 中增加基于菜单 id 查问子菜单记录的办法。代码参考如下:
int getChildCount(Integer id);
第三步:在 SysMenuDao 中增加基于菜单 id 删除菜单记录的办法。代码参考如下:
int deleteObject(Integer id);
-
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>
-
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;
}
-
Controller 类实现
- 业务形容与设计实现
在菜单管制层对象中,增加用于解决菜单删除申请的办法。首先在此办法中通过形参接管客户端提交的数据,而后调用业务层对象执行删除操作,最初封装执行后果,并在运行时将响应对象转换为 JSON 格局的字符串,响应到客户端。 - 要害代码设计与实现
第一步:在 SysMenuController 中增加用于执行删除业务的办法。代码如下:
@RequestMapping("doDeleteObject")
public JsonResult doDeleteObject(Integer id){sysMenuService.deleteObject(id);
return new JsonResult("delete ok");
}
第二步:启动 tomcat 进行拜访测试,关上浏览器输出如下网址:
http://localhost/menu/doDeleteObject?id=10
-
客户端要害业务及代码实现
-
菜单列表页面事件处理
- 业务形容及设计实现
用户在页面上首先抉择要删除的元素,而后点击删除按钮,将用户抉择的记录 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 所示:
-
筹备菜单编辑页面
首先筹备菜单列表页面(/templates/module/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 所示:
-
服务端要害业务及代码实现
-
Pojo 对象
- 业务形容与设计实现
定义值对象封装查问到的上级菜单 id,name,parentId 信息。
- 要害代码设计与实现
package com.cy.pj.common.pojo;
@Date
public class Node implements Serializable{
private static final long serialVersionUID = -6577397050669133046L;
private Integer id;
private String name;
private Integer parentId;
-
Dao 接口实现
- 业务形容与设计实现
基于申请获取数据库对应的菜单表中的所有菜单 id,name,parentId,一行记录封装为一个 Node 对象,多个 node 对象存储到 List 汇合 - 要害代码设计与实现
在 SysMenuDao 接口中增加,用于查问上级菜单相干信息。要害代码如下:
List<Node> findZtreeMenuNodes();
-
Mapper 映射文件
- 业务形容与设计实现
基于 SysMenuMapper 中办法的定义,编写用于菜单查问的 SQL 元素。
- 要害代码设计与实现
在 SysMenuMapper.xml 中增加 findZtreeMenuNodes 元素,用于查问上级菜单信息。要害代码如下:
<select id="findZtreeMenuNodes"
resultType="com.cy.pj.common.vo.Node">
select id,name,parentId
from sys_menus
</select>
-
Service 接口及实现类
- 业务形容与设计实现
基于用户申请,通过数据层对象获取上级菜单相干信息。
- 要害代码实现
第一步:在 SysMenuService 接口中,增加查问菜单信息的办法。要害代码如下:
List<Node> findZtreeMenuNodes()
第二步:在 SysMenuServiceImpl 类中增加,查问菜单信息办法的实现。要害代码如下:
@Override
public List<Node> findZtreeMenuNodes() {return sysMenuDao.findZtreeMenuNodes();
}
-
Controller 类实现
- 业务形容与设计实现
基于客户端申请, 拜访业务层对象办法, 获取菜单节点对象, 并封装返回。
- 要害代码设计与实现
@RequestMapping("doFindZtreeMenuNodes")
public JsonResult doFindZtreeMenuNodes(){return new JsonResult(sysMenuService.findZtreeMenuNodes());
}
-
客户端要害业务及代码实现
-
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"> <a class="layui-layer-ico layui-layer-close layui-layer-close1 btn-cancel" ></a></span>
<div class="layui-layer-btn layui-layer-btn-">
<a class="layui-layer-btn0 btn-confirm"> 确定 </a>
<a class="layui-layer-btn1 btn-cancel"> 勾销 </a>
</div>
</div>
-
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)
});
-
菜单数据增加实现
-
数据根本架构剖析
用户在菜单编辑页面输出数据,而后异步提交到服务端,其繁难数据传递根本架构,如图 -14 所示:
用户在菜单增加页面中填写好菜单数据,而后点击保留按钮,将用户填写的数据增加到数据库。其时序剖析,如图 -15 所示:
-
服务端要害业务及代码实现
-
Pojo 类定义
- 业务形容与设计实现
定义长久化对象,封装客户端申请数据,并将数据传递到数据层进行长久化。
- 要害代码设计与实现
菜单长久层对象类型定义,要害代码如下:
package com.cy.pj.sys.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class SysMenu implements Serializable {
private static final long serialVersionUID = -3366721992602387074L;
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;
}
-
DAO 接口定义
- 业务形容与设计实现
负责将用户提交的菜单数据,长久化到数据库。
- 要害代码设计与实现
在 SysMenuDao 接口中定义数据长久化办法:
int insertObject(SysMenu entity);
-
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>
-
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;
}
-
Controller 类定义
- 业务形容与设计实现
接管客户端提交的菜单数据,并对其进行封装,而后调用业务层对象进行业务解决,最初将业务层处理结果响应到客户端。
- 要害代码设计与实现
定义 Controller 办法,借助此办法解决保留菜单数据申请和响应逻辑。要害代码如下:
@RequestMapping("doSaveObject")
public JsonResult doSaveObject(SysMenu entity){sysMenuService.saveObject(entity);
return new JsonRehlt("保留胜利");
}
-
客户端要害业务及代码实现
-
页面 cancel 按钮事件处理
- 业务形容与设计实现
点击页面 cancel 按钮时,加载菜单那列表页面。
- 要害代码设计与实现
第一步:事件注册(页面加载实现当前)
$(".box-footer")
.on("click",".btn-cancel",doCancel)
第二步:事件处理函数定义
function doCancel(){
var url="menu/menu_list";
$("#mainContentId").load(url);
}
-
页面 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;
}
-
菜单批改页面数据出现
-
业务时序剖析
当在菜单列表页面中选中某条记录,而后点击批改按钮时,其业务时序剖析如图 -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 所示:
-
服务端要害业务及代码实现
-
DAO 接口实现
- 业务形容与设计实现
负责将用户编辑页面提交到服务端的菜单数据,更新到数据库进行持久性存储。
- 要害代码设计与实现
在 SysMenuDao 接口中增加数据更新办法,要害代码如下:
int updateObject(SysMenu entity);
-
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>
-
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;
}
-
Controller 类定义
- 业务形容与设计实现
接管客户端提交的菜单数据,并对其进行封装,而后调用业务层对象进行业务解决,最初将业务层处理结果响应到客户端。
- 要害代码设计与实现
定义 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)
-
FAQ 剖析
- 菜单表是如何设计的,都有哪些字段?
- 菜单列表数据在客户端是如何展现的?(TreeGrid)
- 菜单删除业务是如何解决的?
- 菜单编辑页面中上级菜单数据的出现形式?(zTree)
- 罕用表连贯形式,如图 -18 所示:
-
BUG 剖析
- 有效参数异样(IllegalArgumentException),,如图 -19 所示:
问题剖析:查看以后执行的业务,其后果映射配置,是否将 resultType 写成了 resultMap。
- 菜单编辑页面,上级菜单树结构出现,如图 -20 所示:
问题剖析:查看查问后果中是否有 parentId,或映射对象 Node 中 parentId 是否写错。
- 属性值注入失败,如图 -21 所示:
问题剖析:查看 Spring 容器中是否有 SysMenuService 接口的实现类对象,因为在 SysMenuController 中须要一个这样的对象。