动吧旅游项目 菜单管理模块 part 3

35 篇文章 0 订阅

业务设计说明
菜单管理又称为资源管理,是系统资源对外的表现形式。本模块主要是实现对菜单进行添加、修改、查询、删除等操作。
菜单表与角色表是多对多的关系,在表设计时,多对多关系通常由中间表(关系表)进行维护,如图:
在这里插入图片描述
基于角色菜单表的设计,其角色和菜单对应的关系数据要存储到关系表中,其具体存
储形式,如图:
在这里插入图片描述
原型设计说明
基于用户需求,实现菜单静态页面(html/css/js),通过静态页面为用户呈现菜单模块的基本需求实现。
当在主页左侧菜单栏,点击菜单管理时,在主页内容呈现区,呈现菜单列表页面,如
图:
在这里插入图片描述
当在菜单列表页面点击添加按钮时,异步加载菜单编辑页面,并在列表内容呈现区,
呈现菜单编辑页面,如图
在这里插入图片描述
在菜单编辑页面选择上级菜单时,异步加载菜单信息,并以树结构的形式呈现上级菜
单,如图
在这里插入图片描述
说明:假如客户对此原型进行了确认,后续则可以基于此原型进行研发。
API设计说明
菜单管理业务后台API分层架构及调用关系如图
在这里插入图片描述
说明:分层目的主要将复杂问题简单化,实现各司其职,各尽所能。
菜单管理列表页面呈现
1.业务时序分析
菜单管理页面的加载过程,其时序分析如图
在这里插入图片描述
2.服务端实现
共性代码的提取:
a)SysLogController中:
当controller中每个方法的返回值都有@responseBody,可以将@responseBody放于类上,这时可以用

**@RestController//@RestController等效于@Controller加@ResponseBody**

b)GlobalExceptionHanderler中:
1)@ControllerAdvice 注解描述的类,spring mvc会认为它是一个控制层全局异常处理对象。
2) @ExceptionHandler 注解描述的方法为异常处理方法(不是我们定的),
此注解中定义的异常类型,为这个方法可以处理的异常类型,它可以处理 此异常以及这个异常类型的子类类型的异常。

 **@RestControllerAdvice//等效于@ControllerAdvice加上@ResponseBody**

GlobalExceptionHandler

package com.cy.pj.common.web;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.cy.pj.common.pojo.JsonResult;

/**
 * @ControllerAdvice 注解描述的类,spring mvc会认为它是一个控制层全局异常处理对象。
 */
//@ControllerAdvice
//@ResponseBody 
@RestControllerAdvice //等效于@ControllerAdvice+@ResponseBody 
public class GlobalExceptionHandler {
      /**
       * @ExceptionHandler 注解描述的方法为异常处理方法(不是我们定的),
               *   此注解中定义的异常类型,为这个方法可以处理的异常类型,它可以处理
               *   此异常以及这个异常类型的子类类型的异常。
       * @param e 此参数用于接收具体异常对象,其类型一般与@ExceptionHandler
               *   注解中定义异常类型相同或者为其父类类型。
       * @return 封装了异常信息的对象
       */
	  @ExceptionHandler(RuntimeException.class)
	  //告诉spring mvc最终将响应结果转换为json格式进行输出。
	 // @ResponseBody 
	  public JsonResult doHandleRuntimeException(RuntimeException e) {
		  //方法内部实现异常处理
		  //例如:输出异常,封装异常
		  //输出或打印异常
		  e.printStackTrace();
		  //封装异常信息
		  return new JsonResult(e);
	  }
	  //。。。。。。。。。。。
	  
}

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

	}

分析:
在这里插入图片描述
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.菜单列表页面
本页面呈现菜单信息时要以树结构形式进行呈现。此树结构会借助jquery中的treeGrid插件进行实现,所以在列表页面需要引入treeGrid相关JS。但是,具体的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>

菜单管理列表数据呈现
1.数据架构分析
在这里插入图片描述
说明:本模块将从数据库查询到的菜单数据封装到map对象,一行记录一个map对象,其中key为表中的字段(列)名,值为字段(列)对应的值。
2.数据加载过程其时序分析,如图
在这里插入图片描述
3.服务端关键业务及代码实现
3.1 SysMenuDao

package com.cy.pj.sys.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Mapper;

/** 
* 查询所有菜单信息以及菜单对应的上级菜单名称
*/
@Mapper
public interface SysMenuDao {
	List<Map<String,Object>> findObjects();
}

3.2 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">
  	<!-- 查询所有菜单以及菜单对应的上级菜单名称 -->
  	<select id="findObjects" resultType="map">
  		<!-- 方案1:嵌套查询 -->
  		
  		<!-- select c.*,(

                    select p.name

                    from sys_menus p

                    where c.parentId=p.id

                    ) parentName

          from sys_menus c
           -->
        <!-- 方案2: 多表查询中自关联查询 左外关联-->  
         select c.*,p.name parentName

          from sys_menus c left join sys_menus p

          on c.parentId=p.id 
          
  	</select>
  	
  </mapper>

说明:自关联查询分析,如图
在这里插入图片描述
3.3 Service接口及实现类
第一步:定义菜单业务接口及方法,暴露外界对菜单业务数据的访问,其代码参考如下:

package com.cy.pj.sys.service;

import java.util.List;
import java.util.Map;


public interface SysMenuService {
	List<Map<String,Object>> findObjects();
}

第二步:定义菜单业务接口实现类,并添加菜单业务数据对应的查询操作实现,其代码参考如下:

package com.cy.pj.sys.service.impl;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.service.SysMenuService;


@Service
public class SysMenuServiceImpl implements SysMenuService {
     @Autowired
      private SysMenuDao sysMenuDao;
	@Override
	public List<Map<String, Object>> findObjects() {
		// TODO Auto-generated method stub
		 List<Map<String,Object>> list=sysMenuDao.findObjects();
		 if(list==null||list.size()==0)
             throw new ServiceException("没有对应的菜单信息");
             return list;
	}

}

service层测试类

package com.cy.pj.sys.dao;

import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import lombok.extern.slf4j.Slf4j;

//@Slf4j
@SpringBootTest
public class SysMenuTests {
	//初始化日志对象,假如使用lombok的@Slf4j,底层会自动帮我们生成如下语句
	//设计思想:门面模式
	
	  private static final Logger log=//接口,默认指向的实现类类为logback
	   LoggerFactory.getLogger(SysMenuTests.class);
	 
	@Autowired
	private SysMenuDao sysMenuDao;
	
	@Test
	void testFindObjects() {
		List<Map<String,Object>> list=sysMenuDao.findObjects();
		log.info("list.size{}",list.size());
		/*
		 * for (Map<String, Object> map : list) { System.out.println(map); }
		 */
		
		list.forEach(item->System.out.println(item));//JDK8 Lamda
		list.forEach(System.out::println);//JDK8方法引用
	}

}

关于日志输出说明
在这里插入图片描述

4.Controller类的实现
控制层对象主要负责请求和响应数据的处理,例如,本模块通过业务层对象执行业务逻辑,再通过VO对象封装响应结果(主要对业务层数据添加状态信息),最后将响应结果转换为JSON格式的字符串响应到客户端。

package com.cy.pj.sys.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.sys.service.SysMenuService;

@RequestMapping("/menu/")
@RestController
public class SysMenuController {
	@Autowired
	private SysMenuService sysMenuService;
	
	
	@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//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();

})

菜单管理删除操作实现

在这里插入图片描述
在这里插入图片描述
页面上的删除代码(局部刷新):
在这里插入图片描述
1.业务时序分析
基于用户在列表页面上选择的的菜单记录ID,执行删除操作,本次删除业务实现中,首先要基于id判断当前菜单是否有子菜单,假如有子菜单则不允许删除,没有则先删除菜单角色关系数据,然后再删除菜单自身信息。其时序分析如图
在这里插入图片描述
2.服务端关键业务及代码实现
数据层基于业务层提交的菜单记录id,删除菜单角色关系以及菜单数据,菜单自身记录信息。
第一步:在创建SysRoleMenuDao并定义基于菜单id删除关系数据的方法,关键代码如下:

package com.cy.pj.sys.dao;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
/**
 * 基于此执行角色,菜单关系数据操作
 * */
@Mapper
public interface SysRoleMenuDao {
	//基于菜单id删除角色菜单关系数据
	@Delete(" delete from sys_role_menus  where menu_id=#{menuId} ")
	int deleteObjectsByMenuId(Integer MenuId);
	
}

第二布:SysMenuDao中:

//基于菜单id统计菜单对应的子元素
	@Select("select count(*) from sys_menus  where parentId=#{id} ")
	int getChildCount(Integer id);
	//删除菜单自身信息
	@Delete("delete from sys_menus where id =#{id}")
	int deleteObject(Integer id);

第三步:在SysMenuService中:

//基于菜单id删除角色菜单关系,在删除自身信息
	int deleteObject(Integer id);

第四步:在SysMenuServicImple中

//首先注入SysRoleMenuDao
 @Autowired
     private SysRoleMenuDao sysRoleMenuDao;
     //添加方法
   @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;
	} 

第五步:SysMenuController中:

 @RequestMapping("doDeleteObject")

     public JsonResult doDeleteObject(Integer id){

             sysMenuService.deleteObject(id);

             return new JsonResult("delete ok");

     }

知识点:
1.控制台页面的清空:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
问题分析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

添加功能的实现

在这里插入图片描述
页面的加载:
在这里插入图片描述
加载成功后页面:
在这里插入图片描述
添加页面加载时序分析:
在这里插入图片描述
功能说明:
在这里插入图片描述
首先准备菜单列表页面(/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);

        })

}

ZTree结构:
官网 http://www.treejs.cn/v3/api.php
在这里插入图片描述
在这里插入图片描述
在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>

第一步:引入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)

  });

服务端代码:
Node对象:

package com.cy.pj.common.pojo;

import java.io.Serializable;

import lombok.Data;

@Data
public class Node implements Serializable{ 
	private static final long serialVersionUID = 485127885402380257L;
   private Integer id;
   private String name;
   private Integer parentId;
}

SysMenuDao接口中添加

//查询所有菜单的菜单id,name,parentId
	@Select("select id,name,parentId from sys_menus ")
	List<Node> findZtreeMenuNodes();

SysMenuService接口

List<Node> findZtreeMenuNodes()

SysMenuServiceImpl类中

@Override
    public List<Node> findZtreeMenuNodes() {
    	// TODO Auto-generated method stub
    	 return sysMenuDao.findZtreeMenuNodes();
    }

controller中:

@RequestMapping("doFindZtreeMenuNodes")

public JsonResult doFindZtreeMenuNodes(){

        return new JsonResult(

        sysMenuService.findZtreeMenuNodes());

}


菜单数据添加实现

数据基本架构分析
用户在菜单编辑页面输入数据,然后异步提交到服务端,其简易数据传递基本架构,如图
在这里插入图片描述
用户在菜单添加页面中填写好菜单数据,然后点击保存按钮,将用户填写的数据添加到数据库。其时序分析,如图
在这里插入图片描述
服务端关键业务及代码实现
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;

        }

}

SysMenuDao接口

int insertObject(SysMenu entity);

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>

SysMenuService接口中

int saveObject(SysMenu entity);

SysMenuServiceImpl类中,实现菜单保存操作。

@Override

public int saveObject(SysMenu entity) {

                //1.合法验证

                if(entity==null)

                throw new ServiceException("保存对象不能为空");

                if(StringUtils.isEmpty(entity.getName()))

                throw new ServiceException("菜单名不能为空");

                int rows;

                //2.保存数据

                try{

                rows=sysMenuDao.insertObject(entity);

                }catch(Exception e){

                e.printStackTrace();

                throw new ServiceException("保存失败");

                }

                //3.返回数据

                return rows;

        }

在这里插入图片描述
等效于
在这里插入图片描述
Controller中

@RequestMapping("doSaveObject")

        @ResponseBody

        public JsonResult doSaveObject(SysMenu entity){

                sysMenuService.saveObject(entity);

                return new JsonResult("save ok");

        }

客户端关键业务及代码实现
1.页面cancel按钮事件处理
第一步:事件注册(页面加载完成以后)

 $(".box-footer")

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

第二步:事件处理函数定义

function doCancel(){

        var url="menu/menu_list";

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

  }


2.页面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;

  }

优化:
1.将parentId,id存储到缓存中,在添加或者修改时,菜单某些模块信息直接从缓存中获得,不用与数据库做交互
2.将Map<String,Object>封装成一个对象
3.可以考虑在表中增加冗余字段parentname,这样虽然提高了查询速度但是却提高了维护成本,在修改表时需要同时修改多个数据
一般增加冗余字段时为了增加查询速度,而且不经常修改的数据
在这里插入图片描述
能使用单表不使用连表查询

菜单修改页面数据呈现

数据来源于页面绑定的tr
var data=$(“tbody input📻checked”).parents(“tr”).data(“rowData”);

在这里插入图片描述

业务时序分析
当在菜单列表页面中选中某条记录,然后点击修改按钮时,其业务时序分析如图
在这里插入图片描述
客户端关键业务及代码实现
点击页面修改按钮时,获取选中菜单记录,并异步加载编辑页面。
第一步:列表页面修改按钮事件注册

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

        })

}

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

unction 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);

  }

菜单数据更新实现
在这里插入图片描述
SysMenuDao接口

int updateObject(SysMenu entity);

SysMenuMapper.xml

<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按钮时,将页面上输入的菜单编辑信息提交到服务端。

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

                  }

          });

  }

知识点:
1.在Lombok中,生成构造方法的annotation一共有三个
在这里插入图片描述
a)@NoArgsConstructor : 生成一个无参数的构造方法,这个annotation在与其他的annotation配合起来使用的时候更加能凸显出他的重要性,例如在使用hibernate这种框架的时候,如果有一个有参数的构造方法的时候,NoArgsConstructor会展示出他的作用。
b)@RequiredArgsConstructor: 会生成一个包含常量,和标识了NotNull的变量 的构造方法。生成的构造方法是private,如何想要对外提供使用可以使用staticName选项生成一个static方法。
c)@AllArgsContructor: 会生成一个包含所有变量,同时如果变量使用了NotNull annotation , 会进行是否为空的校验.@AllArgsConstructor 在生成的构造函数上会生成一@ConstructorProperties 的Java annotation, 当然也可以通过将suppressConstructorProperties 设置为true来禁用@ConstructorProperties 。
2.表单数据的获取:
在这里插入图片描述
val():取类型的值,获取的是输入的值
data():获取绑定的数据
3.项目使用map和实体类作为参数的优缺点

map的优点:
1.灵活性强于javaBean,易拓展,耦合性低;
2.写起来简单,代码量少;
3.mybatis查询返回的结果本身就是map,可能会比返回javabean快;

map缺点:
1.javabean在数据输入编译期就会对一些数据类型进行校验,如果出错会直接提示。而map的数据类型则需要到sql层,才会进行处理判断;
2.map的参数名称如果写错,也是需要到sql层,才能判断出是不是字段写错,不利于调试等。相对而言javabean会在编译期间发现错误;
3.map的参数值如果多传、乱传,也是需要到sql层,才能判断出是不是字段写错,不利于调试等。相对而言javabean会在编译期间发现错误;
4.仅仅看方法签名,你不清楚Map中所拥有的参数个数、类型、每个参数代表的含义。 后期人员去维护,例如需要加一个参数等,如果项目层次较多,就需要把每一层的代码都了解清楚才能知道传递了哪些参数。
javabean的优点:
1.面向对象的良好诠释;
2.数据结构清晰,便于团队开发和后期维护;
3.代码足够健壮,可以排除掉编译期错误;
javabean缺点:
1.代码量增多,大量时间去封装用到的表对象;
2.可能会影响开发效率。

4.将项目中数据导出到excel(用阿里的easyexcel)

5.客户端取值
在这里插入图片描述
data是用于存取数据
val()获取input、select的value值,需要有value属性

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自驱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值