如何开发管理后台的导航功能,实现高效便捷的用户体验?

2026-04-19 11:263阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计7848个文字,预计阅读时间需要32分钟。

如何开发管理后台的导航功能,实现高效便捷的用户体验?

1. 功能分析 1.1 查询列表 1.1.1 页面效果 1.1.1.1 功能需求 - 分页查询默认查10条,每页从第1条开始查询 - 默认导航信息仅提供查询按钮 - 非默认导航提供查询、修改、删除按钮 1.1.1.2 新增按钮 - 点击新增按钮弹出新增页面


1.功能分析

1.1. 查询列表

1.1.1. 页面效果

如何开发管理后台的导航功能,实现高效便捷的用户体验?

1.1.2. 功能要求

  • 分页查询默认查询10条每页从第1页开始查询
  • 默认导航信息只提供查询按钮
  • 非默认导航提供查询,修改,删除按钮
  • 点击新增按钮弹出新增导航页面
  • 搜索条件
  • 导航名称:支持模糊搜索
  • 点击搜索按钮是按照录入的搜索条件进行查询数据并渲染
  • 点击重置按钮的时候清空搜索条件,并重新渲染数据

1.2. 新增导航

1.2.1. 页面效果

1.2.2. 功能要求

  • 导航名称,导航链接为必填项
  • 导航名称需做唯一性校验
  • 导航链接需要做唯一性校验
  • 导航图标如果不填则默认填充一个系统定义的默认图标
  • 导航排序如果不填写则默认填写为999(最大放到最后)
  • 导航排序填写范围为[0-999]
  • 是否显示默认选中显示
  • 是否默认导航默认选中否
  • 成功添加数据后列表页进行刷新

1.3. 修改导航

1.3.1. 页面效果

1.3.2. 功能要求

  • 导航名称,默认导航不可修改仅查看
  • 导航链接需做唯一性验证
  • 成功修改数据后列表页进行刷新

1.4. 删除导航

1.4.1. 功能要求

  • 点击删除按钮需给出提示框进行二次确认,当二次确认后可进行删除操作
  • 成功删除数据后列表页进行刷新

1.5. 查询明细

1.5.1. 页面效果

1.5.2. 功能要求

  • 页面仅查看无法进行操作

1.6. 启用/禁用导航

1.6.1. 功能要求

2.功能实现

2.1. 初期准备

2.1.1. 创建数据库 zh_nav

CREATE TABLE `zh_nav` ( `id` int NOT NULL AUTO_INCREMENT, `nav_name` varchar(255) DEFAULT NULL COMMENT '导航名称', `nav_link` varchar(255) DEFAULT NULL COMMENT '导航连接', `nav_icon` varchar(255) DEFAULT NULL COMMENT '导航图标', `nav_sort` int DEFAULT NULL COMMENT '导航排序', `nav_show` tinyint(1) DEFAULT NULL COMMENT '导航是否展示 0.否 1是', `is_default` tinyint(1) DEFAULT NULL COMMENT '是否默认 0否 1是', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `create_user_code` varchar(255) DEFAULT NULL COMMENT '创建人编号', `create_user_name` varchar(255) DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '修改时间', `update_user_code` varchar(255) DEFAULT NULL COMMENT '修改人编号', `update_user_name` varchar(255) DEFAULT NULL COMMENT '修改人名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT COMMENT='导航';

2.1.2. 创建控制层NavController

package com.zhuhuo.modual.controller.manager; import com.zhuhuo.modual.service.NavService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value = "/m/nav") public class NavController { @Autowired private NavService navService; }

2.1.3. 创建实体映射Nav

package com.zhuhuo.modual.entity; import lombok.Data; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import javax.persistence.Table; import java.io.Serializable; import java.util.Date; import javax.persistence.Id; @Data @AllArgsConstructor @NoArgsConstructor @Table(name = "zh_nav") public class Nav implements Serializable { private static final long serialVersionUID = 1L; @Id private Long id; private String navName; private String navLink; private String navIcon; private Integer navSort; private String navType; private Byte navShow; private Long parentId; private Byte isDefault; private Date createTime; private String createUserCode; private String createUserName; private Date updateTime; private String updateUserCode; private String updateUserName; }

2.1.4. 创建NavMapper, NavMapper.xml

package com.zhuhuo.modual.mapper; import com.zhuhuo.core.frame.mapper.BasicsMapper; import com.zhuhuo.modual.entity.Nav; public interface NavMapper extends BasicsMapper<Nav>{ }

<?xml versinotallow="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.zhuhuo.modual.mapper.NavMapper"> <resultMap id="BaseResultMap" type="com.zhuhuo.modual.entity.Nav"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="nav_name" property="navName" jdbcType="VARCHAR"/> <result column="nav_link" property="navLink" jdbcType="VARCHAR"/> <result column="nav_icon" property="navIcon" jdbcType="VARCHAR"/> <result column="nav_sort" property="navSort" jdbcType="INTEGER"/> <result column="nav_type" property="navType" jdbcType="VARCHAR"/> <result column="nav_show" property="navShow" jdbcType="TINYINT"/> <result column="is_default" property="isDefault" jdbcType="TINYINT"/> <result column="parent_id" property="parentId" jdbcType="BIGINT"/> <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/> <result column="create_user_code" property="createUserCode" jdbcType="VARCHAR"/> <result column="create_user_name" property="createUserName" jdbcType="VARCHAR"/> <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/> <result column="update_user_code" property="updateUserCode" jdbcType="VARCHAR"/> <result column="update_user_name" property="updateUserName" jdbcType="VARCHAR"/> </resultMap> <sql id="base_column_list"> id, nav_name, nav_link, nav_icon, nav_sort, nav_type, nav_show,is_default, parent_id,create_time, create_user_code, create_user_name, update_time, update_user_code, update_user_name </sql> </mapper>

2.1.5. 创建NavService ,NavServiceImpl

package com.zhuhuo.modual.service; import com.zhuhuo.modual.entity.Nav; public interface NavService { }

package com.zhuhuo.modual.service.impl; import com.zhuhuo.modual.entity.Nav; import com.zhuhuo.modual.mapper.NavMapper; import com.zhuhuo.modual.service.NavService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("navService") public class NavServiceImpl implements NavService { @Autowired private NavMapper navMapper; }

2.2. 接口设计

请参考接口设计文档

2.3. 查询导航列表

2.3.1. 静态页面

2.3.1.1. 资源引入

1.引入表格插件bootstrap-table的 css和 js文件

2.3.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <th:block th:replace="/manager/common/common :: core-head('导航列表','','')"></th:block> <div th:replace="/manager/common/common :: core-css"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <div class="btn-group-sm" id="toolbar" role="group"> <a class="btn btn-success" id="addBtn" > <i class="fa fa-plus"></i> 新增 </a> </div> <div class="select-table table-striped"> <table id="bootstrap-table-list" ></table> </div> </div> </div> </div> <div th:replace="/manager/common/common :: core-js"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-js"></div> </body> </html>

2.3.1.3. 初始化表格js

$("#bootstrap-table-list").bootstrapTable({ url: "/m/nav/findNavList", // 请求后台的URL(*) contentType: "application/x-www-form-urlencoded", // 编码类型 method: 'get', // 请求方式(*) cache: false, // 是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*) // height: $(window).height()-56, // 表格的高度 striped: true, // 是否显示行间隔色 //排序相关属性 sortable: true, // 是否启用排序 sortStable: true, // 设置为 true 将获得稳定的排序 sortOrder: 'asc', // 排序方式 asc 或者 desc //分页相关属性 pagination: true, // 是否开启分页 sidePagination: 'server', // 分页方式:client客户端分页,server服务端分页(*) //工具栏相关属性 toolbar: '#toolbar', // 指定工作栏 iconSize: 'undefined', // 图标大小:undefined默认的按钮尺寸 xs超小按钮sm小按钮lg大按钮 showFullscreen: true, // 是否显示全屏按钮 showRefresh: true, // 是否显示刷新按钮 showToggle: true, // 是否显示详细视图和列表视图的切换按钮 showColumns: true, // 是否显示所有的列(选择显示的列) //加载相关属性 showLoading: false, // 是否启用加载框 //搜索相关属性 clickToSelect: true, // 是否启用点击选中行 uniqueId: 'id', // 唯一标识符 pageNumber: 1, //初始化加载第一页,默认第一页 pageSize: 10, //每页的记录行数(*) pageList: [10, 25, 50, 100], //可供选择的每页的行数(*) showSearch:true, // 在加载服务器发送来的数据之前处理函数 responseHandler: function(res){ if(res == null || res == 'undefined'){ console.log('resp', res) }else { if (res.responseCode == '200') { return {rows: res.responseData, total: res.total}; } else { $.modal.msg(res.responseMessage,'warning'); return {rows: [], total: 0}; } } }, queryParams:function (params){ var param = { pageSize:params.limit, pageNum: params.offset / params.limit + 1, navName: $("#navName").val(), }; return param; }, queryParamsType:'limit', onLoadSuccess: function (res){ console.log('data', res); }, onLoadError: function (res){ console.log('res', res); }, columns: [ { checkbox: true }, { field: 'id', title: 'id' }, { field: 'navName', title: '导航名称' }, { field: 'isDefault', title: '默认导航', formatter: function (value, item, index) { if (item.isDefault == '1') { return '是'; } else { return '否'; } } }, { field: 'navShow', title: '是否展示', align: 'center', formatter: function (value, item, index) { return statusTools(item); } }, { field: 'navLink', title: '导航链接' }, { title: '操作', align: 'center', formatter: function actionFormatter(value, item) { if(item.isDefault == '1'){ return [ '<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="查看明细" data-width="720" data-height="450" notallow="viewPage('+item.id+')"><i class="fa fa-search"></i></button>', ].join(' ') } else { let btnArr = []; btnArr.push('<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="查看明细" data-width="720" data-height="450" notallow="viewPage('+item.id+')"><i class="fa fa-search"></i></button>'); btnArr.push('<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="编辑" data-width="720" data-height="450" notallow="editPage('+item.id+')"><i class="fa fa-pencil"></i></button>'); btnArr.push('<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="删除" notallow="remove('+item.id+')"><i class="fa fa-times"></i></button>'); return btnArr.join(" "); } }, } ], }); function statusTools(item) { console.log(item) if (item.navShow == 1) { return '<i class=\"fa fa-toggle-off text-info fa-2x\" notallow="enable(\'' + item.id + '\')"></i> '; } else { return '<i class=\"fa fa-toggle-on text-danger fa-2x\" notallow="disable(\'' + item.id + '\')"></i> '; } }

2.3.1.3. sidebar修改

<li> <a class="zh-menu-item" th:href="@{/m/nav/findNavPage}"> <i class="fa fa-paper-plane"></i>导航管理 </a> </li>

2.3.2. 列表功能

2.3.2.1. 创建查询列表页面方法

/** * 导航管理页面 * @return */ @GetMapping(value = "/findNavPage") public String findNavPage(){ return "/manager/nav/list"; }

2.3.2.2.创建请求对象和响应对象

2.3.2.2.1. 请求对象 NavListBO

@Data @NoArgsConstructor @AllArgsConstructor public class NavListBO { /** * 导航名称 */ private String navName; /** * 分页数量 */ private Integer pageNum; /** * 分页条数 */ private Integer pageSize; }

2.3.2.2.2. 响应对象 NavListDTO

@Data @NoArgsConstructor @AllArgsConstructor public class NavListDTO { /** * */ private String id; /** * 导航名称 */ private String navName; /** * 导航连接 */ private String navLink; /** * 导航图标 */ private String navIcon; /** * 导航排序 */ private Integer navSort; /** * 导航是否展示 0.否 1是 */ private Byte navShow; /** * 是否默认导航 0.否 1是 */ private Byte isDefault; }

2.3.2.3. 创建查询列表明细方法

2.3.2.3.1. controller

@ResponseBody @GetMapping(value = "/findNavList") public RespJsonPageData<NavListDTO> findNavList(NavListBO navListBO){ return navService.findNavList(navListBO); }

2.3.2.3.2. service

RespJsonPageData<NavListDTO> findNavList(NavListBO navListBO);

2.3.2.3.3. serviceImpl

public RespJsonPageData<NavListDTO> findNavList(NavListBO navListBO) { PageQuery query = new PageQuery(navListBO); Page<Object> result = PageHelper.startPage(query.getPageNum(), query.getPageSize()); List<Nav> navList = navMapper.findNavList(MapUtils.objToMap(navListBO)); List<NavListDTO> navListDTOList = JacksonUtil.transformList(JacksonUtil.transformJSONCompact(navList), NavListDTO.class); return RespJsonPageData.success(navListDTOList, result.getTotal()); }

2.3.2.3.4. mapper

List<Nav> findNavList(Map<String, Object> params);

2.3.2.3.5. xml

<select id="findNavList" parameterType="java.util.Map" resultMap="BaseResultMap"> select <include refid="base_column_list"/> from zh_nav <include refid="search_list_condition"/> order by nav_sort asc </select> <sql id="search_list_condition"> <where> <if test="navName != null and navName != '' "> and nav_name = like concat('%',#{navName},'%') </if> </where> </sql>

2.3.2.3. 测试查询列表

2.3.3. 条件搜索/重置

2.3.3.1. 静态页面

<div class="panel"> <div class="panel-body"> <form role="search-form" class="form-inline" id="search-form"> <div class="form-group"> <label class="control-label">导航名称</label> <input type="text" placeholder="请输导航名称" id="navName" name="navName" class="form-control"> </div> <a class="btn btn-primary" id="searchBtn" notallow="search()"> <i class="fa fa-search"></i>搜索 </a> <a class="btn btn-warning" id="resetBtn" notallow="reset()"> <i class="fa fa-refresh"></i>重置 </a> </form> </div> </div>

2.3.3.1. 搜索/重置js

/** * 搜索方法 */ function search(){ var params = $("#bootstrap-table-list").bootstrapTable('getOptions'); params.navName = $("#navName").val(); $("#bootstrap-table-list").bootstrapTable('refresh', params); } /** * 重置方法 */ function reset(){ var params = $("#bootstrap-table-list").bootstrapTable('getOptions'); $("#navName").val(''); $("#bootstrap-table-list").bootstrapTable('refresh', params); }

2.4. 新增导航

2.4.1. 静态页面

2.4.1.1. list页面add按钮弹出层

function add(){ let title = '新增导航'; let url = '/m/nav/addNavPage'; let width = 800; let height = $(window).height(); let isFull = true; //判断是否为移动端 if(navigator.userAgent.match(/(iPhone|iPad|Android|ios)/i)){ width = 'auto'; height = 'auto'; } openAddWindow(title,url,width,height,isFull); } function openAddWindow(title, url, width, height,isFull){ var index = top.layer.open({ type: 2, title: title, area: [width + 'px', height + 'px'], content: url, //要展示的内容,此处可以通过后台获取,也可以自定义 fix: false, maxmin: true, //开启最大化最小化按钮 shade: 0.3, //弹层的遮罩 设置遮罩深色背景的透明度 btn: ['确定', '关闭'], shadeClose: true, // 弹层外区域关闭 yes: function(index, layero) { var iframeWin = layero.find('iframe')[0]; //监听当前页layer.open监听页面提交 iframeWin.contentWindow.submitHandler(index, layero); }, cancel: function(index) { return true; }, success: function () { $(':focus').blur(); } }); if(isFull){ top.layer.full(index); } }

2.4.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <div th:replace="/manager/common/common :: core-head('新增文章','','')"></div> <div th:replace="/manager/common/common :: core-css"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <!-- 表单信息--> <form class="form-horizontal m" id="add-nav-form" style="padding-left: 20px;padding-right: 20px"> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航名称</span> <input type="text" class="form-control" placeholder="请输入导航名称" name="navName" id="navName"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航链接</span> <input type="text" class="form-control" placeholder="请输入导航链接" name="navLink" id="navLink"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航图标</span> <input type="text" class="form-control" placeholder="请输入导航图标" name="navIcon" id="navIcon"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航排序</span> <input type="text" class="form-control" placeholder="请输入导航图标" name="navSort" id="navSort"> </div> </div> </div> <div class="form-group"> <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">是否显示</span> <select class="form-control m-b" name="navShow" id="navShow"> <option value="1" selected>显示</option> <option value="2">隐藏</option> </select> </div> </div> <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">默认导航</span> <select class="form-control m-b" name="isDefault" id="isDefault"> <option value="0" selected>否</option> <option value="1">是</option> </select> </div> </div> </div> </form> </div> </div> </div> </div> <div th:replace="/manager/common/common :: core-js"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-js"></div> <div th:replace="/manager/common/common :: lib-jquery-validate-js"></div> </body> </html>

2.4.1.3. 表单校验js

$('#add-nav-form').validate({ onkeyup: false, rules: { navName: { required: true, remote: { url: "/m/nav/validataNavName", type: "post", dataType: "json", data: { "navName" : function() { return trim($("#navName").val()); } }, dataFilter: function(res) { return uniqueCheck(res) } } }, }, messages: { navName: { required: "请录入导航名称", remote:"导航名称已存在,请重新录入", }, }, focusCleanup: true });

2.4.1.4. 表单提交js

function submitHandler(index) { if($("#add-nav-form").validate().form()){ let requestData = formToJsonJq('add-nav-form'); $.ajax({ url:"/m/nav/addNav", type:"post", data:JSON.stringify(requestData), dataType:"json", contentType: "application/json", success: function(result) { var parent = activeWindow(); if(result.responseCode == "200"){ top.layer.close(index); parent.layer.msg(content, { icon: $.modal.icon(1), time: 1000, shift: 5 }); parent.refreshData(); }else { parent.layer.msg(content, { icon: $.modal.icon(2), time: 1000, shift: 5 }); } } }) } } function activeWindow(){ //获取上一层窗口 let topWindow = $(window.parent.document) //获取tab选中的窗体信息 let checkedWindowId = $('.page-tabs-content',topWindow).find('.active').attr('data-id') //判断是否选中tab窗体,如果未选中则直接返回父窗体 if(!checkedWindowId){ return window.parent } //返回上级选中的窗体 return $('.zh-iframe[data-id="'+checkedWindowId+'"]',topWindow)[0].contentWindow; }

2.4.1.5. 工具js

/** * form转json 通过formid传递 * @param formId 表单id * @return {{}} */ function formToJsonJq (formId) { var json = {}; $.each($("#" + formId).serializeArray(), function(i, field) { if (json[field.name]) { json[field.name] += ("," + field.value); } else { json[field.name] = field.value; } }); return json; } /** * form转json 通过传递form * @param form 表单信息 */ function formToJsonNormal(form){ let formData = new FormData(form); let json = {}; for (var [key, value] of formData.entries()) { json[key] = value; } return JSON.stringify(json); } /** * 剔除空格 * @param textval * @return {string} */ function trim(textval){ if (textval == null) { return ""; } return textval.toString().replace(/(^\s*)|(\s*$)|\r|\n/g, ""); } /** * 校验是否为空 * @param textval 传入的文本 */ function isEmpty(textval){ if (textval == null || this.trim(textval) == "" || textval == undefined || textval == "undefined") { return true; } return false; }

2.4.2. 新增功能

2.4.2.1. 创建新增请求对象BO

@Data @NoArgsConstructor @AllArgsConstructor public class NavBO { /** * */ private Long id; /** * 导航名称 */ private String navName; /** * 导航连接 */ private String navLink; /** * 导航图标 */ private String navIcon; /** * 导航排序 */ private Integer navSort; /** * 导航是否展示 0.否 1是 */ private Byte navShow; /** * 是否默认导航 0.否 1是 */ private Byte isDefault; }

2.4.2.2. 创建新增页面方法

@GetMapping(value = "/addNavPage") public String addNavPage(){ return "/manager/nav/add"; }

2.4.2.3. 创建新增页面方法

2.4.2.3.1. controller

@GetMapping(value = "/addNavPage") public String addNavPage(){ return "/manager/nav/add"; } @ResponseBody @PostMapping(value = "/addNav") public RespJson addNav( @RequestBody NavBO navBO){ return navService.addNav(navBO); }

2.4.2.3.2. service,serviceImpl

RespJson addNav(NavBO navBO);

public RespJson addNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getNavName())){ return RespJson.fail(BizResponseCode.NAV_NAME_NOT_EMPTY.getResponseCode(),BizResponseCode.NAV_NAME_NOT_EMPTY.getResponseMessage()); } if(ObjectUtil.isEmpty(navBO.getNavLink())){ return RespJson.fail(BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseCode(),BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseMessage()); } //校验导航名称的唯一性 if(navMapper.findNavCountByNavName(navBO.getNavName()) > 0){ return RespJson.fail(BizResponseCode.NAV_NAME_ALREADY_EXISTS.getResponseCode(),BizResponseCode.NAV_NAME_ALREADY_EXISTS.getResponseMessage()); } if(navMapper.findNavCountByNavLink(navBO.getNavLink()) > 0){ return RespJson.fail(BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseCode(),BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseMessage()); } if(ObjectUtil.isNotEmpty(navBO.getNavSort()) && (navBO.getNavSort() > 999 || navBO.getNavSort() < 0 )){ return RespJson.fail(BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseCode(),BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseMessage()); } Nav nav = new Nav(); nav.setNavName(navBO.getNavName()); nav.setNavLink(navBO.getNavLink()); nav.setNavShow(ObjectUtil.isEmpty(navBO.getNavShow()) ? 0:navBO.getNavShow()); nav.setIsDefault(ObjectUtil.isEmpty(navBO.getIsDefault())? 0:navBO.getIsDefault()); nav.setNavIcon(ObjectUtil.isEmpty(navBO.getNavIcon())? "fa fa-home":navBO.getNavIcon()); nav.setNavSort(ObjectUtil.isEmpty(navBO.getNavSort()) ? 999: navBO.getNavSort()); navMapper.insertSelective(nav); return RespJson.success(); }

2.4.2.3.3. BizResponseCode

NAV_NAME_NOT_EMPTY("102001","导航名称不能为空"), NAV_LINK_NOT_EMPTY("102002","导航链接不能为空"), NAV_NAME_ALREADY_EXISTS("102003","导航名称已存在"), NAV_LINK_ALREADY_EXISTS("102004","导航链接已存在"), NAV_SORT_RANGE_ERROR("102005","导航排序范围只能为[0,999]"),

2.4.2.3.4. mapper,xml

/** * <h2>根据导航名称查询数量</h2> * @param navName * @return */ int findNavCountByNavName(@Param("navName") String navName); /** * <h2>根据导航链接查询数量</h2> * @param navLink * @return */ int findNavCountByNavLink(@Param("navLink") String navLink);

<select id="findNavCountByNavName" parameterType="java.lang.String" resultType="java.lang.Integer"> select count(1) from zh_nav where nav_name = #{navName} </select> <select id="findNavCountByNavLink" parameterType="java.lang.String" resultType="java.lang.Integer"> select count(1) from zh_nav where nav_link = #{navLink} </select>

2.4.2.4. postmant测试新增


2.5. 修改导航

2.5.1. 静态页面

2.5.1.1. list页面edit按钮弹出层

function edit(id){ let title = '修改导航'; let url = '/m/nav/editNavPage/'+id; let width = 800; let height = $(window).height(); let isFull = true; //判断是否为移动端 if(navigator.userAgent.match(/(iPhone|iPad|Android|ios)/i)){ width = 'auto'; height = 'auto'; } openEditWindow(title,url,width,height,isFull); } function openEditWindow(title, url, width, height,isFull){ var index = top.layer.open({ type: 2, title: title, area: [width + 'px', height + 'px'], content: url, //要展示的内容,此处可以通过后台获取,也可以自定义 fix: false, maxmin: true, //开启最大化最小化按钮 shade: 0.3, //弹层的遮罩 设置遮罩深色背景的透明度 btn: ['确定', '关闭'], shadeClose: true, // 弹层外区域关闭 yes: function(index, layero) { var iframeWin = layero.find('iframe')[0]; //监听当前页layer.open监听页面提交 iframeWin.contentWindow.submitHandler(index, layero); }, cancel: function(index) { return true; }, success: function () { $(':focus').blur(); } }); if(isFull){ top.layer.full(index); } }

2.5.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <div th:replace="/manager/common/common :: core-head('新增文章','','')"></div> <div th:replace="/manager/common/common :: core-css"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <!-- 表单信息--> <form class="form-horizontal m" id="edit-nav-form" style="padding-left: 20px;padding-right: 20px" th:object="${editNav}"> <input name="id" type="hidden" th:field="*{id}" /> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航名称</span> <input type="text" class="form-control" placeholder="请输入导航名称" name="navName" id="navName" th:field="*{navName}" disabled> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航链接</span> <input type="text" class="form-control" placeholder="请输入导航链接" name="navLink" id="navLink" th:field="*{navLink}"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航图标</span> <input type="text" class="form-control" placeholder="请输入导航图标" name="navIcon" id="navIcon" th:field="*{navIcon}"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航排序</span> <input type="text" class="form-control" placeholder="请输入导航排序" name="navSort" id="navSort" th:field="*{navSort}"> </div> </div> </div> <div class="form-group" > <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">是否显示</span> <select class="form-control m-b" name="navShow" id="navShow" th:field="*{navShow}"> <option value="1">显示</option> <option value="0">隐藏</option> </select> </div> </div> <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">默认导航</span> <select class="form-control m-b" name="isDefault" id="isDefault" th:field="*{isDefault}" disabled> <option value="0" selected>否</option> <option value="1">是</option> </select> </div> </div> </div> </form> </div> </div> </div> </div> <div th:replace="/manager/common/common :: core-js"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-js"></div> <div th:replace="/manager/common/common :: lib-jquery-validate-js"></div>

2.5.1.3. 表单校验js

$('#edit-nav-form').validate({ onkeyup: false, rules: { navLink: { required: true, }, }, messages: { // custom messages for radio buttons and checkboxes navLink: { required: "请录入导航链接", }, }, focusCleanup: true });

2.5.1.4. 表单提交js

function submitHandler(index) { if($("#edit-nav-form").validate().form()){ let requestData = formToJsonJq('edit-nav-form'); $.ajax({ url:"/m/nav/editNav", type:"post", data:JSON.stringify(requestData), dataType:"json", contentType: "application/json", success: function(result) { var parent = activeWindow(); if(result.responseCode == "200"){ top.layer.close(index); parent.layer.msg(content, { icon: $.modal.icon(1), time: 1000, shift: 5 }); parent.refreshData(); }else { parent.layer.msg(content, { icon: $.modal.icon(2), time: 1000, shift: 5 }); } } }) } } function activeWindow(){ //获取上一层窗口 let topWindow = $(window.parent.document) //获取tab选中的窗体信息 let checkedWindowId = $('.page-tabs-content',topWindow).find('.active').attr('data-id') //判断是否选中tab窗体,如果未选中则直接返回父窗体 if(!checkedWindowId){ return window.parent } //返回上级选中的窗体 return $('.zh-iframe[data-id="'+checkedWindowId+'"]',topWindow)[0].contentWindow; }

2.5.2. 修改功能

2.5.2.1. 创建修改明细响应对象DTO

@Data public class NavDetailDTO { private Long id; /** * 导航名称 */ private String navName; /** * 导航链接 */ private String navLink; /** * 导航图标 */ private String navIcon; /** * 导航排序 */ private Integer navSort; /** * 是否默认导航 0否 1是 */ private Byte isDefault; /** * 导航是否展示 0隐藏 1显示 */ private Byte navShow; }

2.5.2.2. 创建修改页面方法

2.5.2.2.1 controller

@GetMapping(value = "/editNavPage/{id}") public String editNavPage(@PathVariable("id") Long id,ModelMap modelMap){ return navService.editNavPage(id,modelMap); }

2.5.2.2.2 service ,serviceImpl

String editNavPage(Long id, ModelMap modelMap);

@Override public String editNavPage(Long id, ModelMap modelMap) { Nav nav = navMapper.selectByPrimaryKey(id); NavDetailDTO navDetailDTO = JacksonUtil.transformObject(JacksonUtil.transformJSONCompact(nav),NavDetailDTO.class); modelMap.put("editNav",navDetailDTO); return "/manager/nav/edit"; }

2.5.2.3. 创建修改页面方法

2.5.2.3.1. controller

@ResponseBody @PostMapping(value = "/editNav") public RespJson editNav(@RequestBody NavBO navBO){ return navService.editNav(navBO); }

2.5.2.3.2. service,serviceImpl

RespJson editNav(NavBO navBO);

public RespJson editNav(NavBO navBO) { //验证id是否存在 if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } //获取导航信息 Nav nav = navMapper.selectByPrimaryKey(navBO.getId()); if(ObjectUtil.isEmpty(navBO.getNavLink())){ return RespJson.fail(BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseCode(),BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseMessage()); } //验证导航是否存在 if(ObjectUtil.isEmpty(nav)){ return RespJson.fail(BizResponseCode.NAV_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_NOT_EXIST.getResponseMessage()); } if(ObjectUtil.isNotEmpty(navBO.getNavLink()) && !(navBO.getNavLink().equals(nav.getNavLink()))){ if(navMapper.findNavCountByNavLink(navBO.getNavLink()) > 0){ return RespJson.fail(BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseCode(),BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseMessage()); } } if(ObjectUtil.isNotEmpty(navBO.getNavSort()) && (navBO.getNavSort() > 999 || navBO.getNavSort() < 0 )){ return RespJson.fail(BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseCode(),BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseMessage()); } nav.setNavLink(navBO.getNavLink()); nav.setNavSort(navBO.getNavSort()); nav.setNavShow(navBO.getNavShow()); navMapper.updateByPrimaryKeySelective(nav); return RespJson.success(); }

2.5.2.3.3. BizResponseCode

NAV_ID_NOT_EXIST("102006","导航id不存在,请检查"), NAV_NOT_EXIST("102007","导航不存在"),

2.5.2.4. postmant测试修改


2.6. 查询导航明细

2.6.1. 静态页面

2.6.1.1. list页面查询按钮弹出层

function viewPage(id){ let title = '修改导航'; let url = '/m/nav/viewNavPage/'+id; let width = 800; let height = $(window).height(); let isFull = true; //判断是否为移动端 if(navigator.userAgent.match(/(iPhone|iPad|Android|ios)/i)){ width = 'auto'; height = 'auto'; } openViewWindow(title,url,width,height,isFull); } function openViewWindow(title, url, width, height,isFull){ var index = top.layer.open({ type: 2, title: title, area: [width + 'px', height + 'px'], content: url, //要展示的内容,此处可以通过后台获取,也可以自定义 fix: false, maxmin: true, //开启最大化最小化按钮 shade: 0.3, //弹层的遮罩 设置遮罩深色背景的透明度 btn: [ '关闭'], shadeClose: true, // 弹层外区域关闭 cancel: function(index) { return true; }, success: function () { $(':focus').blur(); } }); if(isFull){ top.layer.full(index); } }

2.6.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <div th:replace = "/manager/common/common :: core-head('查询导航','zhuhuo-blog,烛火博客,blog','')"></div> <div th:replace = "/manager/common/common :: core-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <form class="form-horizontal m" id="view-form" style="padding-left: 20px;padding-right: 20px" th:object="${viewNav}"> <input type="hidden" name="id" th:field="*{id}"> <!-- 导航名称 --> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航名称</span> <input type="text" placeholder="请输入导航名称" class="form-control" id="navName" name="navName" th:field="*{navName}" disabled> </div> </div> </div> <!-- 导航链接--> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航链接</span> <input type="text" placeholder="请输入导航链接" class="form-control" id="navLink" name="navLink" th:field="*{navLink}"> </div> </div> </div> <!-- 导航图标 fa fa-home --> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航图标</span> <input type="text" placeholder="请输入导航图标" class="form-control" id="navIcon" name="navIcon" th:field="*{navIcon}"> </div> </div> </div> <!-- 导航排序--> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航排序</span> <input type="text" placeholder="请输入导航排序" class="form-control" id="navSort" name="navSort" th:field="*{navSort}"> </div> </div> </div> <!-- 是否显示/是否默认导航--> <div class="form-group"> <!-- 是否显示--> <div class="col-md-6"> <div class="input-group m-b"> <span class="input-group-addon">是否显示</span> <select class="form-control m-b" name="navShow" id="navShow" th:field="*{navShow}"> <option value="1" selected>显示</option> <option value="0">隐藏</option> </select> </div> </div> <!-- 是否默认导航--> <div class="col-md-6"> <div class="input-group m-b"> <span class="input-group-addon">默认导航</span> <select class="form-control m-b" name="isDefault" id="isDefault" th:field="*{isDefault}" disabled> <option value="0" selected>否</option> <option value="1">是</option> </select> </div> </div> </div> </form> </div> </div> </div> <div th:replace = "/manager/common/common :: core-js"></div> </body> </html>

2.6.2. 查询功能

2.6.2.1. 创建查询页面方法

2.6.2.2.1 controller

@GetMapping(value = "/viewNavPage/{id}") public String viewNavPage(@PathVariable("id") Long id,ModelMap modelMap){ return navService.viewNavPage(id,modelMap); }

2.6.2.2.2 service ,serviceImpl

String viewNavPage(Long id, ModelMap modelMap);

@Override public String viewNavPage(Long id, ModelMap modelMap) { Nav nav = navMapper.selectByPrimaryKey(id); NavDetailDTO navDetailDTO = JacksonUtil.transformObject(JacksonUtil.transformJSONCompact(nav),NavDetailDTO.class); modelMap.put("viewNav",navDetailDTO); return "/manager/nav/view"; }

2.7. 删除导航

2.7.1. 静态页面

2.7.1.1. list页面remove操作

function remove(id){ layer.confirm('确定吗?', { icon: 3, title: "系统提示", btn: ['确认', '取消'] },function(index){ // do something var requestData = { "id": id }; $.ajax({ url:'/m/nav/deleteNav', type:'post', data: JSON.stringify(requestData), dataType:'json', contentType:'application/json', success: function (res){ if(res.responseCode == '200'){ //2.给出响应的消息提示 layer.msg(res.responseMessage,{icon: 1}) //3.刷新bootstrap-table refreshData(); }else { //1.给出对应的消息提示 layer.msg(res.responseMessage, {icon: 2}); } } }) // … layer.close(index); }); }

2.7.2. 删除功能

2.7.2.1. 创建删除方法

2.7.2.2.1. controller

@ResponseBody @PostMapping(value = "/deleteNav") public RespJson deleteNav(@RequestBody NavBO navBO){ return navService.deleteNav(navBO); }

2.7.2.2.2. service,serviceImpl

RespJson deleteNav(NavBO navBO);

@Override public RespJson deleteNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } navMapper.deleteByPrimaryKey(navBO.getId()); return RespJson.success(); }

2.8. 显示/隐藏导航

2.8.1. 静态页面

2.8.1.1. list页面enable操作

function enable(id){ layer.confirm('是否确认启用当前选中的数据', {icon: 3, title:'启用提示'}, function(index){ // do something let requestData = {id:id} $.ajax({ url:'/m/nav/enableNav', type:'post', data: JSON.stringify(requestData), dataType:'json', contentType:'application/json', success: function (res){ if(res.responseCode == '200'){ //2.给出响应的消息提示 layer.msg(res.responseMessage,{icon: 1}) //3.刷新bootstrap-table refreshData(); }else { //1.给出对应的消息提示 layer.msg(res.responseMessage, {icon: 2}); } } }) // … layer.close(index); }); }

2.8.1.2. list页面disable操作

function disable(id){ layer.confirm('是否确认禁用当前选中的数据', {icon: 3, title:'禁用提示'}, function(index){ // do something let requestData = {id:id} $.ajax({ url:'/m/nav/disableNav', type:'post', data: JSON.stringify(requestData), dataType:'json', contentType:'application/json', success: function (res){ if(res.responseCode == '200'){ //2.给出响应的消息提示 layer.msg(res.responseMessage,{icon: 1}) //3.刷新bootstrap-table refreshData(); }else { //1.给出对应的消息提示 layer.msg(res.responseMessage, {icon: 2}); } } }) // … layer.close(index); }); }

2.8.2. 显示/隐藏功能

2.8.2.1. 创建显示/启用方法

2.8.2.1.1. controller

@ResponseBody @PostMapping(value = "/enableNav") public RespJson enableNav(@RequestBody NavBO navBO){ return navService.enableNav(navBO); }

2.8.2.1.2. service,serviceImpl

RespJson enableNav(NavBO navBO);

@Override public RespJson enableNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } Nav nav = navMapper.selectByPrimaryKey(navBO.getId()); if(ObjectUtil.isEmpty(nav)){ return RespJson.fail(BizResponseCode.NAV_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_NOT_EXIST.getResponseMessage()); } nav.setNavShow(ZhuHuoConstant.Status.ENABLE); navMapper.updateByPrimaryKeySelective(nav); return RespJson.success(); }

2.8.2.2. 创建隐藏/禁用方法

2.8.2.2.1. controller

@ResponseBody @PostMapping(value = "/disableNav") public RespJson disableNav(@RequestBody NavBO navBO){ return navService.disableNav(navBO); }

2.8.2.2.2. service,serviceImpl

RespJson disableNav(NavBO navBO);

@Override public RespJson disableNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } Nav nav = navMapper.selectByPrimaryKey(navBO.getId()); if(ObjectUtil.isEmpty(nav)){ return RespJson.fail(BizResponseCode.NAV_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_NOT_EXIST.getResponseMessage()); } nav.setNavShow(ZhuHuoConstant.Status.DISABLE); navMapper.updateByPrimaryKeySelective(nav); return RespJson.success(); }



本文共计7848个文字,预计阅读时间需要32分钟。

如何开发管理后台的导航功能,实现高效便捷的用户体验?

1. 功能分析 1.1 查询列表 1.1.1 页面效果 1.1.1.1 功能需求 - 分页查询默认查10条,每页从第1条开始查询 - 默认导航信息仅提供查询按钮 - 非默认导航提供查询、修改、删除按钮 1.1.1.2 新增按钮 - 点击新增按钮弹出新增页面


1.功能分析

1.1. 查询列表

1.1.1. 页面效果

如何开发管理后台的导航功能,实现高效便捷的用户体验?

1.1.2. 功能要求

  • 分页查询默认查询10条每页从第1页开始查询
  • 默认导航信息只提供查询按钮
  • 非默认导航提供查询,修改,删除按钮
  • 点击新增按钮弹出新增导航页面
  • 搜索条件
  • 导航名称:支持模糊搜索
  • 点击搜索按钮是按照录入的搜索条件进行查询数据并渲染
  • 点击重置按钮的时候清空搜索条件,并重新渲染数据

1.2. 新增导航

1.2.1. 页面效果

1.2.2. 功能要求

  • 导航名称,导航链接为必填项
  • 导航名称需做唯一性校验
  • 导航链接需要做唯一性校验
  • 导航图标如果不填则默认填充一个系统定义的默认图标
  • 导航排序如果不填写则默认填写为999(最大放到最后)
  • 导航排序填写范围为[0-999]
  • 是否显示默认选中显示
  • 是否默认导航默认选中否
  • 成功添加数据后列表页进行刷新

1.3. 修改导航

1.3.1. 页面效果

1.3.2. 功能要求

  • 导航名称,默认导航不可修改仅查看
  • 导航链接需做唯一性验证
  • 成功修改数据后列表页进行刷新

1.4. 删除导航

1.4.1. 功能要求

  • 点击删除按钮需给出提示框进行二次确认,当二次确认后可进行删除操作
  • 成功删除数据后列表页进行刷新

1.5. 查询明细

1.5.1. 页面效果

1.5.2. 功能要求

  • 页面仅查看无法进行操作

1.6. 启用/禁用导航

1.6.1. 功能要求

2.功能实现

2.1. 初期准备

2.1.1. 创建数据库 zh_nav

CREATE TABLE `zh_nav` ( `id` int NOT NULL AUTO_INCREMENT, `nav_name` varchar(255) DEFAULT NULL COMMENT '导航名称', `nav_link` varchar(255) DEFAULT NULL COMMENT '导航连接', `nav_icon` varchar(255) DEFAULT NULL COMMENT '导航图标', `nav_sort` int DEFAULT NULL COMMENT '导航排序', `nav_show` tinyint(1) DEFAULT NULL COMMENT '导航是否展示 0.否 1是', `is_default` tinyint(1) DEFAULT NULL COMMENT '是否默认 0否 1是', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `create_user_code` varchar(255) DEFAULT NULL COMMENT '创建人编号', `create_user_name` varchar(255) DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '修改时间', `update_user_code` varchar(255) DEFAULT NULL COMMENT '修改人编号', `update_user_name` varchar(255) DEFAULT NULL COMMENT '修改人名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT COMMENT='导航';

2.1.2. 创建控制层NavController

package com.zhuhuo.modual.controller.manager; import com.zhuhuo.modual.service.NavService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value = "/m/nav") public class NavController { @Autowired private NavService navService; }

2.1.3. 创建实体映射Nav

package com.zhuhuo.modual.entity; import lombok.Data; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import javax.persistence.Table; import java.io.Serializable; import java.util.Date; import javax.persistence.Id; @Data @AllArgsConstructor @NoArgsConstructor @Table(name = "zh_nav") public class Nav implements Serializable { private static final long serialVersionUID = 1L; @Id private Long id; private String navName; private String navLink; private String navIcon; private Integer navSort; private String navType; private Byte navShow; private Long parentId; private Byte isDefault; private Date createTime; private String createUserCode; private String createUserName; private Date updateTime; private String updateUserCode; private String updateUserName; }

2.1.4. 创建NavMapper, NavMapper.xml

package com.zhuhuo.modual.mapper; import com.zhuhuo.core.frame.mapper.BasicsMapper; import com.zhuhuo.modual.entity.Nav; public interface NavMapper extends BasicsMapper<Nav>{ }

<?xml versinotallow="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.zhuhuo.modual.mapper.NavMapper"> <resultMap id="BaseResultMap" type="com.zhuhuo.modual.entity.Nav"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="nav_name" property="navName" jdbcType="VARCHAR"/> <result column="nav_link" property="navLink" jdbcType="VARCHAR"/> <result column="nav_icon" property="navIcon" jdbcType="VARCHAR"/> <result column="nav_sort" property="navSort" jdbcType="INTEGER"/> <result column="nav_type" property="navType" jdbcType="VARCHAR"/> <result column="nav_show" property="navShow" jdbcType="TINYINT"/> <result column="is_default" property="isDefault" jdbcType="TINYINT"/> <result column="parent_id" property="parentId" jdbcType="BIGINT"/> <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/> <result column="create_user_code" property="createUserCode" jdbcType="VARCHAR"/> <result column="create_user_name" property="createUserName" jdbcType="VARCHAR"/> <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/> <result column="update_user_code" property="updateUserCode" jdbcType="VARCHAR"/> <result column="update_user_name" property="updateUserName" jdbcType="VARCHAR"/> </resultMap> <sql id="base_column_list"> id, nav_name, nav_link, nav_icon, nav_sort, nav_type, nav_show,is_default, parent_id,create_time, create_user_code, create_user_name, update_time, update_user_code, update_user_name </sql> </mapper>

2.1.5. 创建NavService ,NavServiceImpl

package com.zhuhuo.modual.service; import com.zhuhuo.modual.entity.Nav; public interface NavService { }

package com.zhuhuo.modual.service.impl; import com.zhuhuo.modual.entity.Nav; import com.zhuhuo.modual.mapper.NavMapper; import com.zhuhuo.modual.service.NavService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("navService") public class NavServiceImpl implements NavService { @Autowired private NavMapper navMapper; }

2.2. 接口设计

请参考接口设计文档

2.3. 查询导航列表

2.3.1. 静态页面

2.3.1.1. 资源引入

1.引入表格插件bootstrap-table的 css和 js文件

2.3.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <th:block th:replace="/manager/common/common :: core-head('导航列表','','')"></th:block> <div th:replace="/manager/common/common :: core-css"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <div class="btn-group-sm" id="toolbar" role="group"> <a class="btn btn-success" id="addBtn" > <i class="fa fa-plus"></i> 新增 </a> </div> <div class="select-table table-striped"> <table id="bootstrap-table-list" ></table> </div> </div> </div> </div> <div th:replace="/manager/common/common :: core-js"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-js"></div> </body> </html>

2.3.1.3. 初始化表格js

$("#bootstrap-table-list").bootstrapTable({ url: "/m/nav/findNavList", // 请求后台的URL(*) contentType: "application/x-www-form-urlencoded", // 编码类型 method: 'get', // 请求方式(*) cache: false, // 是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*) // height: $(window).height()-56, // 表格的高度 striped: true, // 是否显示行间隔色 //排序相关属性 sortable: true, // 是否启用排序 sortStable: true, // 设置为 true 将获得稳定的排序 sortOrder: 'asc', // 排序方式 asc 或者 desc //分页相关属性 pagination: true, // 是否开启分页 sidePagination: 'server', // 分页方式:client客户端分页,server服务端分页(*) //工具栏相关属性 toolbar: '#toolbar', // 指定工作栏 iconSize: 'undefined', // 图标大小:undefined默认的按钮尺寸 xs超小按钮sm小按钮lg大按钮 showFullscreen: true, // 是否显示全屏按钮 showRefresh: true, // 是否显示刷新按钮 showToggle: true, // 是否显示详细视图和列表视图的切换按钮 showColumns: true, // 是否显示所有的列(选择显示的列) //加载相关属性 showLoading: false, // 是否启用加载框 //搜索相关属性 clickToSelect: true, // 是否启用点击选中行 uniqueId: 'id', // 唯一标识符 pageNumber: 1, //初始化加载第一页,默认第一页 pageSize: 10, //每页的记录行数(*) pageList: [10, 25, 50, 100], //可供选择的每页的行数(*) showSearch:true, // 在加载服务器发送来的数据之前处理函数 responseHandler: function(res){ if(res == null || res == 'undefined'){ console.log('resp', res) }else { if (res.responseCode == '200') { return {rows: res.responseData, total: res.total}; } else { $.modal.msg(res.responseMessage,'warning'); return {rows: [], total: 0}; } } }, queryParams:function (params){ var param = { pageSize:params.limit, pageNum: params.offset / params.limit + 1, navName: $("#navName").val(), }; return param; }, queryParamsType:'limit', onLoadSuccess: function (res){ console.log('data', res); }, onLoadError: function (res){ console.log('res', res); }, columns: [ { checkbox: true }, { field: 'id', title: 'id' }, { field: 'navName', title: '导航名称' }, { field: 'isDefault', title: '默认导航', formatter: function (value, item, index) { if (item.isDefault == '1') { return '是'; } else { return '否'; } } }, { field: 'navShow', title: '是否展示', align: 'center', formatter: function (value, item, index) { return statusTools(item); } }, { field: 'navLink', title: '导航链接' }, { title: '操作', align: 'center', formatter: function actionFormatter(value, item) { if(item.isDefault == '1'){ return [ '<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="查看明细" data-width="720" data-height="450" notallow="viewPage('+item.id+')"><i class="fa fa-search"></i></button>', ].join(' ') } else { let btnArr = []; btnArr.push('<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="查看明细" data-width="720" data-height="450" notallow="viewPage('+item.id+')"><i class="fa fa-search"></i></button>'); btnArr.push('<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="编辑" data-width="720" data-height="450" notallow="editPage('+item.id+')"><i class="fa fa-pencil"></i></button>'); btnArr.push('<button type="button" class="btn btn-sm btn-secondary" data-toggle="tooltip" title="删除" notallow="remove('+item.id+')"><i class="fa fa-times"></i></button>'); return btnArr.join(" "); } }, } ], }); function statusTools(item) { console.log(item) if (item.navShow == 1) { return '<i class=\"fa fa-toggle-off text-info fa-2x\" notallow="enable(\'' + item.id + '\')"></i> '; } else { return '<i class=\"fa fa-toggle-on text-danger fa-2x\" notallow="disable(\'' + item.id + '\')"></i> '; } }

2.3.1.3. sidebar修改

<li> <a class="zh-menu-item" th:href="@{/m/nav/findNavPage}"> <i class="fa fa-paper-plane"></i>导航管理 </a> </li>

2.3.2. 列表功能

2.3.2.1. 创建查询列表页面方法

/** * 导航管理页面 * @return */ @GetMapping(value = "/findNavPage") public String findNavPage(){ return "/manager/nav/list"; }

2.3.2.2.创建请求对象和响应对象

2.3.2.2.1. 请求对象 NavListBO

@Data @NoArgsConstructor @AllArgsConstructor public class NavListBO { /** * 导航名称 */ private String navName; /** * 分页数量 */ private Integer pageNum; /** * 分页条数 */ private Integer pageSize; }

2.3.2.2.2. 响应对象 NavListDTO

@Data @NoArgsConstructor @AllArgsConstructor public class NavListDTO { /** * */ private String id; /** * 导航名称 */ private String navName; /** * 导航连接 */ private String navLink; /** * 导航图标 */ private String navIcon; /** * 导航排序 */ private Integer navSort; /** * 导航是否展示 0.否 1是 */ private Byte navShow; /** * 是否默认导航 0.否 1是 */ private Byte isDefault; }

2.3.2.3. 创建查询列表明细方法

2.3.2.3.1. controller

@ResponseBody @GetMapping(value = "/findNavList") public RespJsonPageData<NavListDTO> findNavList(NavListBO navListBO){ return navService.findNavList(navListBO); }

2.3.2.3.2. service

RespJsonPageData<NavListDTO> findNavList(NavListBO navListBO);

2.3.2.3.3. serviceImpl

public RespJsonPageData<NavListDTO> findNavList(NavListBO navListBO) { PageQuery query = new PageQuery(navListBO); Page<Object> result = PageHelper.startPage(query.getPageNum(), query.getPageSize()); List<Nav> navList = navMapper.findNavList(MapUtils.objToMap(navListBO)); List<NavListDTO> navListDTOList = JacksonUtil.transformList(JacksonUtil.transformJSONCompact(navList), NavListDTO.class); return RespJsonPageData.success(navListDTOList, result.getTotal()); }

2.3.2.3.4. mapper

List<Nav> findNavList(Map<String, Object> params);

2.3.2.3.5. xml

<select id="findNavList" parameterType="java.util.Map" resultMap="BaseResultMap"> select <include refid="base_column_list"/> from zh_nav <include refid="search_list_condition"/> order by nav_sort asc </select> <sql id="search_list_condition"> <where> <if test="navName != null and navName != '' "> and nav_name = like concat('%',#{navName},'%') </if> </where> </sql>

2.3.2.3. 测试查询列表

2.3.3. 条件搜索/重置

2.3.3.1. 静态页面

<div class="panel"> <div class="panel-body"> <form role="search-form" class="form-inline" id="search-form"> <div class="form-group"> <label class="control-label">导航名称</label> <input type="text" placeholder="请输导航名称" id="navName" name="navName" class="form-control"> </div> <a class="btn btn-primary" id="searchBtn" notallow="search()"> <i class="fa fa-search"></i>搜索 </a> <a class="btn btn-warning" id="resetBtn" notallow="reset()"> <i class="fa fa-refresh"></i>重置 </a> </form> </div> </div>

2.3.3.1. 搜索/重置js

/** * 搜索方法 */ function search(){ var params = $("#bootstrap-table-list").bootstrapTable('getOptions'); params.navName = $("#navName").val(); $("#bootstrap-table-list").bootstrapTable('refresh', params); } /** * 重置方法 */ function reset(){ var params = $("#bootstrap-table-list").bootstrapTable('getOptions'); $("#navName").val(''); $("#bootstrap-table-list").bootstrapTable('refresh', params); }

2.4. 新增导航

2.4.1. 静态页面

2.4.1.1. list页面add按钮弹出层

function add(){ let title = '新增导航'; let url = '/m/nav/addNavPage'; let width = 800; let height = $(window).height(); let isFull = true; //判断是否为移动端 if(navigator.userAgent.match(/(iPhone|iPad|Android|ios)/i)){ width = 'auto'; height = 'auto'; } openAddWindow(title,url,width,height,isFull); } function openAddWindow(title, url, width, height,isFull){ var index = top.layer.open({ type: 2, title: title, area: [width + 'px', height + 'px'], content: url, //要展示的内容,此处可以通过后台获取,也可以自定义 fix: false, maxmin: true, //开启最大化最小化按钮 shade: 0.3, //弹层的遮罩 设置遮罩深色背景的透明度 btn: ['确定', '关闭'], shadeClose: true, // 弹层外区域关闭 yes: function(index, layero) { var iframeWin = layero.find('iframe')[0]; //监听当前页layer.open监听页面提交 iframeWin.contentWindow.submitHandler(index, layero); }, cancel: function(index) { return true; }, success: function () { $(':focus').blur(); } }); if(isFull){ top.layer.full(index); } }

2.4.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <div th:replace="/manager/common/common :: core-head('新增文章','','')"></div> <div th:replace="/manager/common/common :: core-css"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <!-- 表单信息--> <form class="form-horizontal m" id="add-nav-form" style="padding-left: 20px;padding-right: 20px"> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航名称</span> <input type="text" class="form-control" placeholder="请输入导航名称" name="navName" id="navName"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航链接</span> <input type="text" class="form-control" placeholder="请输入导航链接" name="navLink" id="navLink"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航图标</span> <input type="text" class="form-control" placeholder="请输入导航图标" name="navIcon" id="navIcon"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航排序</span> <input type="text" class="form-control" placeholder="请输入导航图标" name="navSort" id="navSort"> </div> </div> </div> <div class="form-group"> <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">是否显示</span> <select class="form-control m-b" name="navShow" id="navShow"> <option value="1" selected>显示</option> <option value="2">隐藏</option> </select> </div> </div> <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">默认导航</span> <select class="form-control m-b" name="isDefault" id="isDefault"> <option value="0" selected>否</option> <option value="1">是</option> </select> </div> </div> </div> </form> </div> </div> </div> </div> <div th:replace="/manager/common/common :: core-js"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-js"></div> <div th:replace="/manager/common/common :: lib-jquery-validate-js"></div> </body> </html>

2.4.1.3. 表单校验js

$('#add-nav-form').validate({ onkeyup: false, rules: { navName: { required: true, remote: { url: "/m/nav/validataNavName", type: "post", dataType: "json", data: { "navName" : function() { return trim($("#navName").val()); } }, dataFilter: function(res) { return uniqueCheck(res) } } }, }, messages: { navName: { required: "请录入导航名称", remote:"导航名称已存在,请重新录入", }, }, focusCleanup: true });

2.4.1.4. 表单提交js

function submitHandler(index) { if($("#add-nav-form").validate().form()){ let requestData = formToJsonJq('add-nav-form'); $.ajax({ url:"/m/nav/addNav", type:"post", data:JSON.stringify(requestData), dataType:"json", contentType: "application/json", success: function(result) { var parent = activeWindow(); if(result.responseCode == "200"){ top.layer.close(index); parent.layer.msg(content, { icon: $.modal.icon(1), time: 1000, shift: 5 }); parent.refreshData(); }else { parent.layer.msg(content, { icon: $.modal.icon(2), time: 1000, shift: 5 }); } } }) } } function activeWindow(){ //获取上一层窗口 let topWindow = $(window.parent.document) //获取tab选中的窗体信息 let checkedWindowId = $('.page-tabs-content',topWindow).find('.active').attr('data-id') //判断是否选中tab窗体,如果未选中则直接返回父窗体 if(!checkedWindowId){ return window.parent } //返回上级选中的窗体 return $('.zh-iframe[data-id="'+checkedWindowId+'"]',topWindow)[0].contentWindow; }

2.4.1.5. 工具js

/** * form转json 通过formid传递 * @param formId 表单id * @return {{}} */ function formToJsonJq (formId) { var json = {}; $.each($("#" + formId).serializeArray(), function(i, field) { if (json[field.name]) { json[field.name] += ("," + field.value); } else { json[field.name] = field.value; } }); return json; } /** * form转json 通过传递form * @param form 表单信息 */ function formToJsonNormal(form){ let formData = new FormData(form); let json = {}; for (var [key, value] of formData.entries()) { json[key] = value; } return JSON.stringify(json); } /** * 剔除空格 * @param textval * @return {string} */ function trim(textval){ if (textval == null) { return ""; } return textval.toString().replace(/(^\s*)|(\s*$)|\r|\n/g, ""); } /** * 校验是否为空 * @param textval 传入的文本 */ function isEmpty(textval){ if (textval == null || this.trim(textval) == "" || textval == undefined || textval == "undefined") { return true; } return false; }

2.4.2. 新增功能

2.4.2.1. 创建新增请求对象BO

@Data @NoArgsConstructor @AllArgsConstructor public class NavBO { /** * */ private Long id; /** * 导航名称 */ private String navName; /** * 导航连接 */ private String navLink; /** * 导航图标 */ private String navIcon; /** * 导航排序 */ private Integer navSort; /** * 导航是否展示 0.否 1是 */ private Byte navShow; /** * 是否默认导航 0.否 1是 */ private Byte isDefault; }

2.4.2.2. 创建新增页面方法

@GetMapping(value = "/addNavPage") public String addNavPage(){ return "/manager/nav/add"; }

2.4.2.3. 创建新增页面方法

2.4.2.3.1. controller

@GetMapping(value = "/addNavPage") public String addNavPage(){ return "/manager/nav/add"; } @ResponseBody @PostMapping(value = "/addNav") public RespJson addNav( @RequestBody NavBO navBO){ return navService.addNav(navBO); }

2.4.2.3.2. service,serviceImpl

RespJson addNav(NavBO navBO);

public RespJson addNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getNavName())){ return RespJson.fail(BizResponseCode.NAV_NAME_NOT_EMPTY.getResponseCode(),BizResponseCode.NAV_NAME_NOT_EMPTY.getResponseMessage()); } if(ObjectUtil.isEmpty(navBO.getNavLink())){ return RespJson.fail(BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseCode(),BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseMessage()); } //校验导航名称的唯一性 if(navMapper.findNavCountByNavName(navBO.getNavName()) > 0){ return RespJson.fail(BizResponseCode.NAV_NAME_ALREADY_EXISTS.getResponseCode(),BizResponseCode.NAV_NAME_ALREADY_EXISTS.getResponseMessage()); } if(navMapper.findNavCountByNavLink(navBO.getNavLink()) > 0){ return RespJson.fail(BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseCode(),BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseMessage()); } if(ObjectUtil.isNotEmpty(navBO.getNavSort()) && (navBO.getNavSort() > 999 || navBO.getNavSort() < 0 )){ return RespJson.fail(BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseCode(),BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseMessage()); } Nav nav = new Nav(); nav.setNavName(navBO.getNavName()); nav.setNavLink(navBO.getNavLink()); nav.setNavShow(ObjectUtil.isEmpty(navBO.getNavShow()) ? 0:navBO.getNavShow()); nav.setIsDefault(ObjectUtil.isEmpty(navBO.getIsDefault())? 0:navBO.getIsDefault()); nav.setNavIcon(ObjectUtil.isEmpty(navBO.getNavIcon())? "fa fa-home":navBO.getNavIcon()); nav.setNavSort(ObjectUtil.isEmpty(navBO.getNavSort()) ? 999: navBO.getNavSort()); navMapper.insertSelective(nav); return RespJson.success(); }

2.4.2.3.3. BizResponseCode

NAV_NAME_NOT_EMPTY("102001","导航名称不能为空"), NAV_LINK_NOT_EMPTY("102002","导航链接不能为空"), NAV_NAME_ALREADY_EXISTS("102003","导航名称已存在"), NAV_LINK_ALREADY_EXISTS("102004","导航链接已存在"), NAV_SORT_RANGE_ERROR("102005","导航排序范围只能为[0,999]"),

2.4.2.3.4. mapper,xml

/** * <h2>根据导航名称查询数量</h2> * @param navName * @return */ int findNavCountByNavName(@Param("navName") String navName); /** * <h2>根据导航链接查询数量</h2> * @param navLink * @return */ int findNavCountByNavLink(@Param("navLink") String navLink);

<select id="findNavCountByNavName" parameterType="java.lang.String" resultType="java.lang.Integer"> select count(1) from zh_nav where nav_name = #{navName} </select> <select id="findNavCountByNavLink" parameterType="java.lang.String" resultType="java.lang.Integer"> select count(1) from zh_nav where nav_link = #{navLink} </select>

2.4.2.4. postmant测试新增


2.5. 修改导航

2.5.1. 静态页面

2.5.1.1. list页面edit按钮弹出层

function edit(id){ let title = '修改导航'; let url = '/m/nav/editNavPage/'+id; let width = 800; let height = $(window).height(); let isFull = true; //判断是否为移动端 if(navigator.userAgent.match(/(iPhone|iPad|Android|ios)/i)){ width = 'auto'; height = 'auto'; } openEditWindow(title,url,width,height,isFull); } function openEditWindow(title, url, width, height,isFull){ var index = top.layer.open({ type: 2, title: title, area: [width + 'px', height + 'px'], content: url, //要展示的内容,此处可以通过后台获取,也可以自定义 fix: false, maxmin: true, //开启最大化最小化按钮 shade: 0.3, //弹层的遮罩 设置遮罩深色背景的透明度 btn: ['确定', '关闭'], shadeClose: true, // 弹层外区域关闭 yes: function(index, layero) { var iframeWin = layero.find('iframe')[0]; //监听当前页layer.open监听页面提交 iframeWin.contentWindow.submitHandler(index, layero); }, cancel: function(index) { return true; }, success: function () { $(':focus').blur(); } }); if(isFull){ top.layer.full(index); } }

2.5.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <div th:replace="/manager/common/common :: core-head('新增文章','','')"></div> <div th:replace="/manager/common/common :: core-css"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <!-- 表单信息--> <form class="form-horizontal m" id="edit-nav-form" style="padding-left: 20px;padding-right: 20px" th:object="${editNav}"> <input name="id" type="hidden" th:field="*{id}" /> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航名称</span> <input type="text" class="form-control" placeholder="请输入导航名称" name="navName" id="navName" th:field="*{navName}" disabled> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航链接</span> <input type="text" class="form-control" placeholder="请输入导航链接" name="navLink" id="navLink" th:field="*{navLink}"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航图标</span> <input type="text" class="form-control" placeholder="请输入导航图标" name="navIcon" id="navIcon" th:field="*{navIcon}"> </div> </div> </div> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航排序</span> <input type="text" class="form-control" placeholder="请输入导航排序" name="navSort" id="navSort" th:field="*{navSort}"> </div> </div> </div> <div class="form-group" > <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">是否显示</span> <select class="form-control m-b" name="navShow" id="navShow" th:field="*{navShow}"> <option value="1">显示</option> <option value="0">隐藏</option> </select> </div> </div> <div class=" col-md-6"> <div class="input-group m-b "> <span class="input-group-addon">默认导航</span> <select class="form-control m-b" name="isDefault" id="isDefault" th:field="*{isDefault}" disabled> <option value="0" selected>否</option> <option value="1">是</option> </select> </div> </div> </div> </form> </div> </div> </div> </div> <div th:replace="/manager/common/common :: core-js"></div> <div th:replace="/manager/common/common :: lib-bootstrap-table-js"></div> <div th:replace="/manager/common/common :: lib-jquery-validate-js"></div>

2.5.1.3. 表单校验js

$('#edit-nav-form').validate({ onkeyup: false, rules: { navLink: { required: true, }, }, messages: { // custom messages for radio buttons and checkboxes navLink: { required: "请录入导航链接", }, }, focusCleanup: true });

2.5.1.4. 表单提交js

function submitHandler(index) { if($("#edit-nav-form").validate().form()){ let requestData = formToJsonJq('edit-nav-form'); $.ajax({ url:"/m/nav/editNav", type:"post", data:JSON.stringify(requestData), dataType:"json", contentType: "application/json", success: function(result) { var parent = activeWindow(); if(result.responseCode == "200"){ top.layer.close(index); parent.layer.msg(content, { icon: $.modal.icon(1), time: 1000, shift: 5 }); parent.refreshData(); }else { parent.layer.msg(content, { icon: $.modal.icon(2), time: 1000, shift: 5 }); } } }) } } function activeWindow(){ //获取上一层窗口 let topWindow = $(window.parent.document) //获取tab选中的窗体信息 let checkedWindowId = $('.page-tabs-content',topWindow).find('.active').attr('data-id') //判断是否选中tab窗体,如果未选中则直接返回父窗体 if(!checkedWindowId){ return window.parent } //返回上级选中的窗体 return $('.zh-iframe[data-id="'+checkedWindowId+'"]',topWindow)[0].contentWindow; }

2.5.2. 修改功能

2.5.2.1. 创建修改明细响应对象DTO

@Data public class NavDetailDTO { private Long id; /** * 导航名称 */ private String navName; /** * 导航链接 */ private String navLink; /** * 导航图标 */ private String navIcon; /** * 导航排序 */ private Integer navSort; /** * 是否默认导航 0否 1是 */ private Byte isDefault; /** * 导航是否展示 0隐藏 1显示 */ private Byte navShow; }

2.5.2.2. 创建修改页面方法

2.5.2.2.1 controller

@GetMapping(value = "/editNavPage/{id}") public String editNavPage(@PathVariable("id") Long id,ModelMap modelMap){ return navService.editNavPage(id,modelMap); }

2.5.2.2.2 service ,serviceImpl

String editNavPage(Long id, ModelMap modelMap);

@Override public String editNavPage(Long id, ModelMap modelMap) { Nav nav = navMapper.selectByPrimaryKey(id); NavDetailDTO navDetailDTO = JacksonUtil.transformObject(JacksonUtil.transformJSONCompact(nav),NavDetailDTO.class); modelMap.put("editNav",navDetailDTO); return "/manager/nav/edit"; }

2.5.2.3. 创建修改页面方法

2.5.2.3.1. controller

@ResponseBody @PostMapping(value = "/editNav") public RespJson editNav(@RequestBody NavBO navBO){ return navService.editNav(navBO); }

2.5.2.3.2. service,serviceImpl

RespJson editNav(NavBO navBO);

public RespJson editNav(NavBO navBO) { //验证id是否存在 if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } //获取导航信息 Nav nav = navMapper.selectByPrimaryKey(navBO.getId()); if(ObjectUtil.isEmpty(navBO.getNavLink())){ return RespJson.fail(BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseCode(),BizResponseCode.NAV_LINK_NOT_EMPTY.getResponseMessage()); } //验证导航是否存在 if(ObjectUtil.isEmpty(nav)){ return RespJson.fail(BizResponseCode.NAV_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_NOT_EXIST.getResponseMessage()); } if(ObjectUtil.isNotEmpty(navBO.getNavLink()) && !(navBO.getNavLink().equals(nav.getNavLink()))){ if(navMapper.findNavCountByNavLink(navBO.getNavLink()) > 0){ return RespJson.fail(BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseCode(),BizResponseCode.NAV_LINK_ALREADY_EXISTS.getResponseMessage()); } } if(ObjectUtil.isNotEmpty(navBO.getNavSort()) && (navBO.getNavSort() > 999 || navBO.getNavSort() < 0 )){ return RespJson.fail(BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseCode(),BizResponseCode.NAV_SORT_RANGE_ERROR.getResponseMessage()); } nav.setNavLink(navBO.getNavLink()); nav.setNavSort(navBO.getNavSort()); nav.setNavShow(navBO.getNavShow()); navMapper.updateByPrimaryKeySelective(nav); return RespJson.success(); }

2.5.2.3.3. BizResponseCode

NAV_ID_NOT_EXIST("102006","导航id不存在,请检查"), NAV_NOT_EXIST("102007","导航不存在"),

2.5.2.4. postmant测试修改


2.6. 查询导航明细

2.6.1. 静态页面

2.6.1.1. list页面查询按钮弹出层

function viewPage(id){ let title = '修改导航'; let url = '/m/nav/viewNavPage/'+id; let width = 800; let height = $(window).height(); let isFull = true; //判断是否为移动端 if(navigator.userAgent.match(/(iPhone|iPad|Android|ios)/i)){ width = 'auto'; height = 'auto'; } openViewWindow(title,url,width,height,isFull); } function openViewWindow(title, url, width, height,isFull){ var index = top.layer.open({ type: 2, title: title, area: [width + 'px', height + 'px'], content: url, //要展示的内容,此处可以通过后台获取,也可以自定义 fix: false, maxmin: true, //开启最大化最小化按钮 shade: 0.3, //弹层的遮罩 设置遮罩深色背景的透明度 btn: [ '关闭'], shadeClose: true, // 弹层外区域关闭 cancel: function(index) { return true; }, success: function () { $(':focus').blur(); } }); if(isFull){ top.layer.full(index); } }

2.6.1.2. 页面布局

<!DOCTYPE html> <html lang="en"> <head> <div th:replace = "/manager/common/common :: core-head('查询导航','zhuhuo-blog,烛火博客,blog','')"></div> <div th:replace = "/manager/common/common :: core-css"></div> </head> <body class="gray-bg"> <div class="wrapper wrapper-content"> <div class="panel"> <div class="panel-body"> <form class="form-horizontal m" id="view-form" style="padding-left: 20px;padding-right: 20px" th:object="${viewNav}"> <input type="hidden" name="id" th:field="*{id}"> <!-- 导航名称 --> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航名称</span> <input type="text" placeholder="请输入导航名称" class="form-control" id="navName" name="navName" th:field="*{navName}" disabled> </div> </div> </div> <!-- 导航链接--> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航链接</span> <input type="text" placeholder="请输入导航链接" class="form-control" id="navLink" name="navLink" th:field="*{navLink}"> </div> </div> </div> <!-- 导航图标 fa fa-home --> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航图标</span> <input type="text" placeholder="请输入导航图标" class="form-control" id="navIcon" name="navIcon" th:field="*{navIcon}"> </div> </div> </div> <!-- 导航排序--> <div class="form-group"> <div class="col-md-12"> <div class="input-group m-b"> <span class="input-group-addon">导航排序</span> <input type="text" placeholder="请输入导航排序" class="form-control" id="navSort" name="navSort" th:field="*{navSort}"> </div> </div> </div> <!-- 是否显示/是否默认导航--> <div class="form-group"> <!-- 是否显示--> <div class="col-md-6"> <div class="input-group m-b"> <span class="input-group-addon">是否显示</span> <select class="form-control m-b" name="navShow" id="navShow" th:field="*{navShow}"> <option value="1" selected>显示</option> <option value="0">隐藏</option> </select> </div> </div> <!-- 是否默认导航--> <div class="col-md-6"> <div class="input-group m-b"> <span class="input-group-addon">默认导航</span> <select class="form-control m-b" name="isDefault" id="isDefault" th:field="*{isDefault}" disabled> <option value="0" selected>否</option> <option value="1">是</option> </select> </div> </div> </div> </form> </div> </div> </div> <div th:replace = "/manager/common/common :: core-js"></div> </body> </html>

2.6.2. 查询功能

2.6.2.1. 创建查询页面方法

2.6.2.2.1 controller

@GetMapping(value = "/viewNavPage/{id}") public String viewNavPage(@PathVariable("id") Long id,ModelMap modelMap){ return navService.viewNavPage(id,modelMap); }

2.6.2.2.2 service ,serviceImpl

String viewNavPage(Long id, ModelMap modelMap);

@Override public String viewNavPage(Long id, ModelMap modelMap) { Nav nav = navMapper.selectByPrimaryKey(id); NavDetailDTO navDetailDTO = JacksonUtil.transformObject(JacksonUtil.transformJSONCompact(nav),NavDetailDTO.class); modelMap.put("viewNav",navDetailDTO); return "/manager/nav/view"; }

2.7. 删除导航

2.7.1. 静态页面

2.7.1.1. list页面remove操作

function remove(id){ layer.confirm('确定吗?', { icon: 3, title: "系统提示", btn: ['确认', '取消'] },function(index){ // do something var requestData = { "id": id }; $.ajax({ url:'/m/nav/deleteNav', type:'post', data: JSON.stringify(requestData), dataType:'json', contentType:'application/json', success: function (res){ if(res.responseCode == '200'){ //2.给出响应的消息提示 layer.msg(res.responseMessage,{icon: 1}) //3.刷新bootstrap-table refreshData(); }else { //1.给出对应的消息提示 layer.msg(res.responseMessage, {icon: 2}); } } }) // … layer.close(index); }); }

2.7.2. 删除功能

2.7.2.1. 创建删除方法

2.7.2.2.1. controller

@ResponseBody @PostMapping(value = "/deleteNav") public RespJson deleteNav(@RequestBody NavBO navBO){ return navService.deleteNav(navBO); }

2.7.2.2.2. service,serviceImpl

RespJson deleteNav(NavBO navBO);

@Override public RespJson deleteNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } navMapper.deleteByPrimaryKey(navBO.getId()); return RespJson.success(); }

2.8. 显示/隐藏导航

2.8.1. 静态页面

2.8.1.1. list页面enable操作

function enable(id){ layer.confirm('是否确认启用当前选中的数据', {icon: 3, title:'启用提示'}, function(index){ // do something let requestData = {id:id} $.ajax({ url:'/m/nav/enableNav', type:'post', data: JSON.stringify(requestData), dataType:'json', contentType:'application/json', success: function (res){ if(res.responseCode == '200'){ //2.给出响应的消息提示 layer.msg(res.responseMessage,{icon: 1}) //3.刷新bootstrap-table refreshData(); }else { //1.给出对应的消息提示 layer.msg(res.responseMessage, {icon: 2}); } } }) // … layer.close(index); }); }

2.8.1.2. list页面disable操作

function disable(id){ layer.confirm('是否确认禁用当前选中的数据', {icon: 3, title:'禁用提示'}, function(index){ // do something let requestData = {id:id} $.ajax({ url:'/m/nav/disableNav', type:'post', data: JSON.stringify(requestData), dataType:'json', contentType:'application/json', success: function (res){ if(res.responseCode == '200'){ //2.给出响应的消息提示 layer.msg(res.responseMessage,{icon: 1}) //3.刷新bootstrap-table refreshData(); }else { //1.给出对应的消息提示 layer.msg(res.responseMessage, {icon: 2}); } } }) // … layer.close(index); }); }

2.8.2. 显示/隐藏功能

2.8.2.1. 创建显示/启用方法

2.8.2.1.1. controller

@ResponseBody @PostMapping(value = "/enableNav") public RespJson enableNav(@RequestBody NavBO navBO){ return navService.enableNav(navBO); }

2.8.2.1.2. service,serviceImpl

RespJson enableNav(NavBO navBO);

@Override public RespJson enableNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } Nav nav = navMapper.selectByPrimaryKey(navBO.getId()); if(ObjectUtil.isEmpty(nav)){ return RespJson.fail(BizResponseCode.NAV_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_NOT_EXIST.getResponseMessage()); } nav.setNavShow(ZhuHuoConstant.Status.ENABLE); navMapper.updateByPrimaryKeySelective(nav); return RespJson.success(); }

2.8.2.2. 创建隐藏/禁用方法

2.8.2.2.1. controller

@ResponseBody @PostMapping(value = "/disableNav") public RespJson disableNav(@RequestBody NavBO navBO){ return navService.disableNav(navBO); }

2.8.2.2.2. service,serviceImpl

RespJson disableNav(NavBO navBO);

@Override public RespJson disableNav(NavBO navBO) { if(ObjectUtil.isEmpty(navBO.getId())){ return RespJson.fail(BizResponseCode.NAV_ID_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_ID_NOT_EXIST.getResponseMessage()); } Nav nav = navMapper.selectByPrimaryKey(navBO.getId()); if(ObjectUtil.isEmpty(nav)){ return RespJson.fail(BizResponseCode.NAV_NOT_EXIST.getResponseCode(),BizResponseCode.NAV_NOT_EXIST.getResponseMessage()); } nav.setNavShow(ZhuHuoConstant.Status.DISABLE); navMapper.updateByPrimaryKeySelective(nav); return RespJson.success(); }