zoukankan      html  css  js  c++  java
  • java学习day54--DB旅游生态系统-菜单模块设计

    DB项目-菜单设计模块

    菜单管理设计说明

    业务设计说明

    ​ 菜单管理又称为资源管理,是系统资源对外的表现形式。本模块主要是实现对菜单进行添加、修改、查询、删除等操作。

    表设计1(sys_menus)

    字段 类型 注释
    id int(11) 主键自增
    name varchar(50) 资源名称
    url varchar(200) 资源URL
    type int(11) 类型 1:菜单 2:按钮'
    sort int(11) 排序
    note varchar(100) 备注
    parentId int(11) 父菜单ID,一级菜单为0
    permission varchar(500) 授权(如:sys:user:create)
    createdTime datetime 创建时间
    modifiedTime datetime 修改时间
    createdUser varchar(20) 创建用户
    modifiedUser varchar(20) 修改用户

    ​ 菜单表与角色表是多对多的关系,在表设计时,多对多关系通常由中间表(关系表)进行维护

    1594723019059

    基于角色菜单表的设计,其角色和菜单对应的关系数据要存储到关系表中,其具体存储形式如下:

    1594723081534

    表设计2(sys_role_menus)

    菜单与角色的关系表脚本设计

    字段 类型 含义
    id int(11) 主键自增
    role_id int(11) 角色ID
    menu_id int(11) 菜单ID

    原型设计说明

    ​ 基于用户需求,实现菜单静态页面(html/css/js),通过静态页面为用户呈现菜单模块的基本需求实现。当在主页左侧菜单栏,点击菜单管理时,在主页内容呈现区,呈现菜单列表页面(treegrid树状表格)

    1594723337338

    ​ 当在菜单列表页面点击添加按钮时,异步加载菜单编辑页面,并在列表内容呈现区,呈现菜单编辑页面

    1594723392964

    ​ 在菜单编辑页面选择上级菜单时,异步加载菜单信息,并以树结构的形式呈现上级菜单

    1594723425740

    API设计说明

    1594723495680

    菜单管理列表页面呈现

    业务时序分析

    1594723558656

    服务端实现

    Controller实现

    • 业务描述与设计实现

      基于菜单管理的请求业务,在PageController中添加doMenuUI方法,用于返回菜单列表页面。

    • 关键代码设计与实现

      第一步:在PageController中定义返回菜单列表的方法。

      @RequestMapping("menu/menu_list")	
      public String doMenuUI() {
          return "sys/menu_list";
      }
      

      第二步:在PageController中基于rest风格的url方式优化返回UI页面的方法。(页面跳转通用方法)

      //rest 风格(一种架构风格)的url,其语法结构{变量名}/{变量}
      //PathVariable注解用于修饰方法参数,可以从rest风格的url中取和参数名对应的值
      @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")
      })
      

    菜单列表页面

    • 业务描述与设计实现

      ​ 本页面呈现菜单信息时要以树结构形式进行呈现。此树结构会借助jquery中的treeGrid插件进行实现,所以在菜单列表页面需要引入treeGrid相关JS。

    • 关键代码设计与实现

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

    菜单管理列表数据呈现

    数据架构分析

    ​ 说明:菜单列表页面加载完成,启动菜单数据异步加载操作,本次菜单列表页面要呈现菜单以及菜单对应的父级菜单的名称,其数据查询时,数据的封装及传递过程

    1594724072728

    说明:本模块将从数据库查询到的菜单数据封装到map对象,一行记录一个map对象,其中key为表中的字段(列)名,值为字段(列)对应的值。

    时序分析

    1594724196349

    服务端关键业务及代码实现

    Dao接口实现

    • 业务描述与设计实现

      ​ 通过数据层对象,基于业务层参数,查询菜单以及上级菜单信息(要查询上级菜单名称)。

    • 关键代码设计与实现

      第一步:定义数据层接口对象,通过此对象实现数据库中菜单数据的访问操作。

      第二步:在SysMenuDao接口中添加findObjects方法,基于此方法实现菜单数据的查询操作。

      @Mapper
      public interface SysMenuDao {
      	List<Map<String,Object>> findObjects();
      }
      

      说明:一行记录映射为一个map对象,多行存储到list。

      使用map有什么优势劣势?

      优势:

      1、灵活性强于javabean,易扩展,耦合度低。
      2、写起来简单,代码量少。
      3、mybatis 查询的返回结果本身就是MAP,可能会比返回javabean快
      

      劣势:

      1、javabean在数据输入编译期就会对一些数据类型进行校验,如果出错会直接提示。而map的数据类型则需要到sql层,才会进行处理判断。
      2、map的参数名称如果写错,也是需要到sql层,才能判断出是不是字段写错,不利于调试等。相对而言javabean会在编译期间发现错误
      3、map的参数值如果多传、乱传,也是需要到sql层,才能判断出是不是字段写错,不利于调试等。相对而言javabean会在编译期间发现错误
      4、仅仅看方法签名,你不清楚Map中所拥有的参数个数、类型、每个参数代表的含义。 后期人员去维护,例如需要加一个参数等,如果项目层次较多,就需要把每一层的代码都了解清楚才能知道传递了哪些参数。
      

    Mapper文件实现

    • 业务描述与设计实现

      ​ 基于Dao接口创建映射文件,在此文件中通过相关元素(例如select)描述要执行的数据操作。

    • 关键代码设计与实现

      第一步:在映射文件的设计目录中添加SysMenuMapper.xml映射文件

      第二步:在映射文件中添加id为findObjects的元素,实现菜单记录查询。我们要查询所有菜单以及菜单对应的上级菜单名称。

      <?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">
          <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>
      </mapper>
      

    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//等效于@Controller+ @ResponseBody注解.
      public class SysMenuController {
      	@Autowired
      	private SysMenuService sysMenuService;
      	@RequestMapping("doFindObjects")
      	public JsonResult doFindObjects() {
      		return new  JsonResult(sysMenuService.findObjects());
      	}
          //系统底层会将对象转换为json格式字符串
      	//底层是通过DispatcherServlet对象调用jackson api将对象转换为json串
      	//说明:jackson api 将对象转换为json时会调用对象的get方法取值,并且将
      	//get方法的get单词后面的名字作为key,方法的返回值作为value进行数据的转换。
      }
      

    客户端关键业务及代码实现

    菜单列表信息呈现

    • 业务描述与设计实现

      ​ 菜单页面加载完成以后,向服务端发起异步请求加载菜单信息,当菜单信息加载完成需要将菜单信息呈现到列表页面上。

    • 关键代码设计与实现

      第一步:在菜单列表页面引入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扩展的一个网格树插件)
      
      

      注意:使用该规范的时候需要注意以下约定:客户端的字段必须与服务端的字段对应.

      1594726488075

      第三步:定义异步请求处理函数

      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判断当前菜单是否有子菜单,假如有子菜单则不允许删除,没有则先删除菜单角色关系数据,然后再删除菜单自身信息。

    1594865198512

    服务端关键业务及代码实现

    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>
      

      或者在接口方法直接定义:

      @Delete("delete from sys_role_menus where menu_id=#{menuId}")
      

      第二步:在SysMenuMapper.xml文件中添加基于id统计子菜单数量的元素

      <select id="getChildCount"
              parameterType="int"
              resultType="int">
          select count(*)
          from sys_menus
          where parentId=#{id}        
      </select>
      

      或者在接口方法直接定义:

      @Select("select count(*) from sys_menus where parentId=#{id}")
      

      第三步:在SysMenuMapper.xml文件添加delete元素,基于带单id删除菜单自身记录信息

      <delete id="deleteObject">
          delete from sys_menus
          where id =#{id}
      </delete>
      

      或者在接口方法直接定义:

      @Delete("delete from sys_menus where id=#{id}")
      

    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<1)
              throw new IllegalArgumentException("参数值无效");
          //2.统计菜单子元素并校验。
          int childCount=sysMenuDao.getChildCount(id);
          if(childCount>0)
              throw new ServiceException("请先删除子菜单");
          //3.删除关系数据(角色和菜单关系数据)
          sysRoleMenuDao.deleteObjectsByMenuId(id);
          //4.删除自身信息。
          int rows=sysMenuDao.deleteObject(id);
          if(rows==0)
              throw new ServiceException("记录可能已经不存在");
          return rows;
      }
      

    Controller类实现

    • 业务描述与设计实现

      ​ 在菜单控制层对象中,添加用于处理菜单删除请求的方法。首先在此方法中通过形参接收客户端提交的数据,然后调用业务层对象执行删除操作,最后封装执行结果,并在运行时将响应对象转换为JSON格式的字符串,响应到客户端。

    • 关键代码设计与实现

      第一步:在SysMenuController中添加用于执行删除业务的方法。

      @RequestMapping("doDeleteObject")
      public JsonResult doDeleteObject(Integer id){
          sysMenuService.deleteObject(id);
          //假如在调用业务方法时出现了异常,下面的return肯不执行了
          return new JsonResult("delete ok");
      }
      

    客户端关键业务及代码实现

    菜单列表页面事件处理

    • 业务描述与设计实现

      ​ 用户在页面上首先选择要删除的元素,然后点击删除按钮,将用户选择的记录id异步提交到服务端,最后在服务端执行菜单的删除动作。

    • 关键代码设计与实现

      第一步:页面加载完成以后,在删除按钮上进行点击事件注册。

      $(".input-group-btn")
      	.on("click",".btn-delete",doDeleteObject);
      

      第二步:定义删除操作对应的事件处理函数。

      function doDeleteObject(){
        //1.获取选中记录的id值
        var id=doGetCheckedId();
        console.log("id="+id);
        if(!id){
      	  alert("请先选中");
      	  return;
        }
        //2.给出提示是否确认删除
        if(!confirm("确认删除吗"))return;
        //3.发送异步请求执行删除操作
        //3.1定义请求参数
        var params={"id":id};
        //3.2定义请求url
        var url="menu/doDeleteObject";
        //3.3发送异步请求
        $.post(url,params,function(result){
      	  if(result.state==1){
      		  alert(result.message);
      		  $("tbody input:radio:checked")
      		  .parents("tr").remove();
      	  }else{
      		  alert(result.message);
      	  }
        })
      }
      

      第三步:定义获取用户选中的记录id的函数。

      function doGetCheckedId(){
      	//方法1:
      	//var radio=$("tbody input:radio:checked");
      	//if(radio)return radio.val();
      	//方法2:
      	//1.获取选中的记录
      	var selections=$("#menuTable")
      	//bootstrapTreeTable是treeGrid插件内部定义的jquery扩展函数
      	//getSelections为扩展函数内部要调用的一个方法
      	.bootstrapTreeTable("getSelections");
      	//2.对记录进行判定
      	if(selections.length==1)
      	return selections[0].id;
      }
      

    菜单添加页面呈现

    业务时序分析

    1594866790243

    菜单编辑页面呈现

    • 业务描述与设计实现

      ​ 菜单列表页面点击添加按钮时,异步加载菜单编辑页面。

    • 关键代码设计与实现

      第一步:菜单列表页面上,对添加按钮进行事件注册

      $(function(){
      	$(".input-group-btn")
          .on("click",".btn-add,.btn-update",doLoadEditUI);
      })
      

      第二步:定义添加按钮事件处理函数

      function doLoadEditUI(){
      	var title;
      	//基于点击对象的class属性值,修改标题
      	if($(this).hasClass("btn-add")){
      		title="添加菜单";
      	}
      	//异步加载编辑页面
      	var url="menu/menu_edit";
      	$("#mainContentId").load(url,function(){
      		$(".box-title").html(title);
      	})
      }
      

    菜单编辑页面上级菜单呈现

    业务时序分析

    在菜单编辑页面上,点击上级菜单时,其数据加载时序分析

    1594868797423

    服务端关键业务及代码实现

    Node对象

    • 业务描述与设计实现

      定义值对象封装查询到的上级菜单id,name,parentId信息。

    • 关键代码设计与实现

    package com.cy.pj.common.pojo;
    @Data
    public class Node implements Serializable{
    	private static final long serialVersionUID = 9102665096902784699L;
    	private Integer id;
    	private String name;
    	private Integer parentId;
    }
    

    Dao接口实现

    • 业务描述与设计实现

      ​ 基于请求获取数据库对应的菜单表中的所有菜单id,name,parentId,一行记录封装为一个Node对象,多个node对象存储到List集合

    • 关键代码设计与实现

    在SysMenuDao接口中添加,用于查询上级菜单相关信息。

    //获取所有菜单的菜单id,name,parentId。
    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>
      

      或者在接口方法上直接使用注解:

      @Select("select id,name,parentId from sys_menus")
      

    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组件

      <!-- zTree 对应的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;  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; //zTree是第三方扩展的一个Jquery插件
      //初始化zTree时会用到
      var setting = {
          data : {
              simpleData : {
                  enable : true,//表示使用简单数据模式
                  idKey : "id",  //节点数据中保存唯一标识的属性名称,与查询的字段相对应
                  pIdKey : "parentId",  
                  //节点数据中保存其父节点唯一标识的属性名称,与查询的字段相对应
                  rootPId : null  //根节点id
              }//json 格式javascript对象
          }
      }//json 格式的javascript对象
      

      第三步:定义异步加载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);//id,name,parentId
                  //doRemoveNodeFromZtree();//修改时,可考虑此方案
                  //显示zTree对应的div
                  $("#menuLayer").css("display","block");
              }else{
                  alert(result.message);
              }
          })
      }
      

      第四步:定义zTree中取消按钮事件处理函数,点击取消隐藏zTree

      //zTree取消按钮事件处理函数
        function doHideTree(){
      		 $("#menuLayer").css("display","none");
        }
      

      第五步:定义zTree中确定按钮对应的事件处理处理函数。

      function doSetSelectNode(){
          //1.获取选中的节点对象
          var nodes=zTree.getSelectedNodes();
          if(nodes.length==1){	  
              var selectedNode=nodes[0];
              console.log("selectNode",selectedNode);
              var rowData=$("#mainContentId").data("rowData");
              if(rowData){//修改时做如下处理
                  //判定当前选中的上级菜单节点是否为当前要修改节点的子节点.
                  var flag=isChild(rowData.id,selectedNode);
                  if(flag){
                      alert("不能选择当前节点以及对应子节点");
                      return;
                  }
              }
              //2.将对象中内容,填充到表单
              $("#parentId").data("parentId",selectedNode.id);
              $("#parentId").val(selectedNode.name);
          }
          //3.隐藏树对象
          doHideTree();
      }
      

      第六步:定义页面加载完成以后的事件处理函数

      $(function(){
      	  $(".form-horizontal")//事件不能注册到$("#mainContentId")对象上
      	  .on("click",".load-sys-menu",doLoadZtreeNodes);
      	  
      	  $("#menuLayer")
            .on("click",".btn-confirm",doSetSelectNode)
            .on("click",".btn-cancel",doHideTree);
      });
      

    菜单数据添加实现

    数据基本架构分析

    ​ 用户在菜单编辑页面输入数据,然后异步提交到服务端,其简易数据传递基本架构

    1594870331159

    业务时序分析

    用户在菜单添加页面中填写好菜单数据,然后点击保存按钮,将用户填写的数据添加到数据库。

    1594870399083

    服务端关键业务及代码实现

    POJO类定义

    • 业务描述与设计实现

      定义持久化对象,封装客户端请求数据,并将数据传递到数据层进行持久化。

    • 关键代码设计与实现

      菜单持久层对象类型定义

      package com.cy.pj.sys.pojo;
      
      @Data
      public class SysMenu implements Serializable{
      	private static final long serialVersionUID = -7691821439056974454L;
      	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 sysMenu);
      

    Mapper映射文件

    • 业务描述与设计实现

      ​ 基于SysMenuDao中方法的定义,编写用于实现菜单添加的SQL元素。

    • 关键代码设计与实现

      ​ 在SysMenuMapper.xml中添加insertObject元素,用于写入菜单信息。

      <insert id="insertObject">
          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);
          return rows;
      }
      

    Controller类定义

    • 业务描述与设计实现

      ​ 接收客户端提交的菜单数据,并对其进行封装,然后调用业务层对象进行业务处理,最后将业务层处理结果响应到客户端。

    • 关键代码设计与实现

      ​ 定义Controller方法,借助此方法处理保存菜单数据请求和响应逻辑。

      @RequestMapping("doSaveObject")
      public JsonResult doSaveObject(SysMenu entity) {
          sysMenuService.saveObject(entity);
          return new JsonResult("save ok");
      }
      

    客户端关键业务及代码实现

    页面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();
          var rowData=
              $("#mainContentId").data("rowData");//获取要修改的数据
          //2.异步提交表单数据(post)
          var insertUrl="menu/doSaveObject";//添加操作执行
          var updateUrl="menu/doUpdateObject";//修改操作执行
          var url=rowData?updateUrl:insertUrl;
          if(rowData)params.id=rowData.id;
          $.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;
        }
      

    菜单修改页面数据呈现

    业务时序分析

    ​ 当在菜单列表页面中选中某条记录,然后点击修改按钮时,其业务时序分析

    1594874433715

    客户端关键业务及代码实现

    列表页面修改按钮事件处理

    • 业务描述与设计实现

      ​ 点击页面修改按钮时,获取选中菜单记录,并异步加载编辑页面。

    • 关键代码设计与实现

      第一步:列表页面修改按钮事件注册

       $(".input-group-btn")
      		.on("click",".btn-update",doLoadEditUI);
      

      第二步:修改按钮事件处理函数定义或修改

      function doLoadEditUI(){
      	var title;
      	//基于点击对象的class属性值,修改标题
      	if($(this).hasClass("btn-add")){
      		title="添加菜单";
      	}else{
      		title="修改菜单";
      		var item=doGetCheckedItem();
      		if(!item){
      			alert("请先选择");
      			return;
      		}
      		$("#mainContentId")
      		.data("rowData",item);
      	}
      	//异步加载编辑页面
      	var url="menu/menu_edit";
      	$("#mainContentId").load(url,function(){
      		$(".box-title").html(title);
      	})
      }
      

      第三步:获取用户选中记录的函数定义。

      function doGetCheckedItem(){
      	return $("tbody input[type='radio']:checked")
      	.parents("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);
      }
      

    菜单数据更新实现

    业务时序分析

    1594875335499

    服务端关键业务及代码实现

    DAO接口实现

    • 业务描述与设计实现

      ​ 负责将用户编辑页面提交到服务端的菜单数据,更新到数据库进行持久性存储。

    • 关键代码设计与实现

      ​ 在SysMenuDao接口中添加数据更新方法

      int updateObject(SysMenu entity);
      

    Mapper映射文件定义

    • 业务描述与设计实现

      ​ 基于SysMenuDao中updateObject方法的定义,编写用于实现菜单更新的SQL元素。

    • 关键代码设计与实现

      ​ 在SysMenuMapper.xml中添加updateObject元素,用于更新菜单信息。

      <update id="updateObject"
              parameterType="com.cy.pj.sys.pojo.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) {
          if (entity==null) {
              throw new ServiceException("修改后的对象不能为空");
          }
          if (StringUtils.isEmpty(entity.getName())) {
              throw new ServiceException("菜单名不能为空");
          }
          int rows = sysMenuDao.updateObject(entity);
          if (rows==0) {
              throw new ServiceException("记录可能不存在啦");
          }
          return rows;
      }
      

    Controller类定义

    • 业务描述与设计实现

      ​ 接收客户端提交的菜单数据,并对其进行封装,然后调用业务层对象进行业务处理,最后将业务层处理结果响应到客户端。

    • 关键代码设计与实现

      ​ 定义Controller方法,借助此方法处理保存菜单数据请求和响应逻辑。

      @RequestMapping("doUpdateObject")
      public JsonResult doUpdateObject(SysMenu sysMenu) {
          sysMenuService.updateObject(sysMenu);
          return new JsonResult("update ok");
      }
      

    客户端关键业务及代码实现

    编辑页面更新按钮事件处理

    • 业务描述与设计实现

      ​ 点击页面save按钮时,将页面上输入的菜单编辑信息提交到服务端。

    • 关键代码设计与实现

      编辑Save按钮对应的事件处理函数。

      function doSaveOrUpdate(){
      	  //1.获取表单数据
      	   var params=doGetEditFormData();
      	   var rowData=
      	   $("#mainContentId").data("rowData");
      	  //2.异步提交表单数据(post)
      	   var insertUrl="menu/doSaveObject";
      	   var updateUrl="menu/doUpdateObject";
      	   var url=rowData?updateUrl:insertUrl;
      	   if(rowData)params.id=rowData.id;
      	   $.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;
        }
      

      至此菜单模块完毕,bug分析见下一章

  • 相关阅读:
    LCT男人八题系列
    hadoop 伪分布启动-fs格式化
    hadoop 安装
    Scala Actor入门
    Scala 隐式转换和隐式参数
    Scala 类型参数
    Scala 类型参数
    Scala 匹配模式
    scala 函数式编程之集合操作
    Scala 函数式编程
  • 原文地址:https://www.cnblogs.com/liqbk/p/13332795.html
Copyright © 2011-2022 走看看