1. 修正
1.1 登录完成跳转到后台主页面改成重定向
@RequestMapping("/admin/do/login")
public String doLogin(@RequestParam("loginAcct") String loginAcct,
@RequestParam("userPswd") String userPswd,
Model model,
HttpSession session){
//调用adminService的login方法执行登陆业务逻辑,返回查询到的Admin对象
Admin admin = adminService.login(loginAcct,userPswd);
//判断admin是否为null
if(admin == null){
model.addAttribute(CrowdFundingConstant.ATTR_NAME_MESSAGE, CrowdFundingConstant.MESSAGE_LOGIN_FAILED);
return "admin-login";
}
session.setAttribute(CrowdFundingConstant.ATTR_NAME_LOGIN_ADMIN,admin);
return "redirect:/admin/to/main/page.html";
}
说明:/admin/to/main/page.html地址是在view-controller中配置的。
2. 后台主页面完整显示
2.1 加入原型页面源代码
原型/main.html→admin-main.jsp
需要调整的内容:
- 字符集改成UTF-8
- 加入title和base标签
- 把“张三”改成${sessionScope['LOGIN-ADMIN'].userName }
- 把退出的超链接改成实际地址
退出系统
2.2 公共部分提取
-
head标签部分
<%@ include file="/WEB-INF/include-head.jsp" %>
-
nav标签部分
<%@ include file="/WEB-INF/include-nav.jsp" %>
-
sidebar部分
<%@ include file="/WEB-INF/include-sidebar.jsp" %>
2.3 创建JSP模版
2.3.1 模版内容
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/include-head.jsp" %>
<%@ include file="/WEB-INF/include-nav.jsp" %>
<%@ include file="/WEB-INF/include-sidebar.jsp" %>
2.3.2 创建JSP模版操作
3. Admin维护功能清单
- 分页显示全部Admin数据√
- 分页显示Admin数据的关键词查询结果√
- 批量删除
- 单条删除
- 新增Admin
- 更新Admin
分页显示全部数据和查询结果在后端可以合并为同一个操作。
批量删除和单条删除在后端可以合并为同一个操作。
4. 分页功能
4.1 分析
4.2 执行查询的SQL语句
SELECT
*
FROM
t_admin
WHERE
login_acct LIKE CONCAT("%", "", "%")
OR user_name LIKE CONCAT("%", "", "%")
OR email LIKE CONCAT("%", "", "%");
4.3 MyBatis的PageHelper插件
4.3.1 作用
以完全非侵入的方式在原有查询基础上附加分页效果。从SQL层面来说,在SQL语句后面附加LIMIT子句。从Java代码来说,把原来返回的List类型封装为Page类型。
4.3.2 依赖信息
com.github.pagehelper
pagehelper
4.0.0
com.github.pagehelper
pagehelper
4.3.3 配置方式
- 所在工程:atcrowdfunding-admin-1-webui
- 配置文件:spring-persist-mybatis.xml
- 在SqlSessionFactoryBean中配置MyBatis插件
mysql
true
4.4 AdminMapper
4.4.1 Mapper配置文件
- 所在工程:atcrowdfunding-admin-1-webui
- 文件:resources/mybatis/mapper/AdminMapper.xml
4.4.2 Mapper接口
- 所在工程:atcrowdfunding-admin-2-component
- 全类名:com.rgh.crowd.funding.mapper.AdminMapper
List selectAdminListByKeyword(String keyword);
4.5 AdminService
所在工程:atcrowdfunding-admin-2-component
接口方法:
PageInfo queryForKeywordSearch(Integer pageNum, Integer pageSize, String keyword);
实现类方法:
@Override
public PageInfo queryForKeywordSearch(Integer pageNum, Integer pageSize, String keyword) {
// 1.调用PageHelper的工具方法,开启分页功能
PageHelper.startPage(pageNum, pageSize);
// 2.执行分页查询
List list = adminMapper.selectAdminListByKeyword(keyword);
// 3.将list封装到PageInfo对象中
return new PageInfo<>(list);
}
4.6 AdminHandler
- 所在工程:atcrowdfunding-admin-2-component
- 全类名:com.rgh.crowd.funding.handler.AdminHandler
@RequestMapping("/admin/query/for/search")
public String queryForSearch(
// 如果页面上没有提供对应的请求参数,那么可以使用defaultValue指定默认值
@RequestParam(value="pageNum", defaultValue="1") Integer pageNum,
@RequestParam(value="pageSize", defaultValue="5") Integer pageSize,
@RequestParam(value="keyword", defaultValue="") String keyword,
Model model) {
PageInfo pageInfo = adminService.queryForKeywordSearch(pageNum, pageSize, keyword);
model.addAttribute(CrowdFundingConstant.ATTR_NAME_PAGE_INFO, pageInfo);
return "admin-page";
}
4.7 插入测试用的假数据
- 所在工程:atcrowdfunding-admin-1-webui
- 全类名:com.rgh.crowd.funding.test.CrowdFundingTest
@Test
public void batchSaveAdmin() {
for(int i = 0; i < 500; i++) {
adminMapper.insert(new Admin(null, "loginAcct"+i, "1111111", "userName"+i, "email"+i+"@qq.com", null));
}
}
4.8 页面显示:主体部分
抱歉!没有符合您要求的查询结果!
${myStatus.count }
${admin.loginAcct }
${admin.userName }
${admin.email }
4.9 页面显示:导航条部分
使用一个基于jQuery的分页插件:Pagination
4.9.1 环境搭建
-
加入样式文件
将pagination.css复制到/atcrowdfunding-admin-1-webui/src/main/webapp/css目录下
-
在需要的页面引用pagination.css
在admin-page.jsp中引入
-
加入Pagination的js文件
将jquery.pagination.js复制到/atcrowdfunding-admin-1-webui/src/main/webapp/script目录下
-
在需要的页面引用jquery.pagination.js
4.9.2 分页导航条需要在HTML标签中加入的部分
4.9.3 jQuery代码
$(function() {
// 对分页导航条显示进行初始化
initPagination();
});
// 声明函数封装导航条初始化操作
function initPagination() {
// 声明变量存储总记录数
var totalRecord = ${requestScope['PAGE-INFO'].total};
// 声明变量存储分页导航条显示时的属性设置
var paginationProperties = {
num_edge_entries : 3, //边缘页数
num_display_entries : 5, //主体页数
callback : pageselectCallback, //回调函数
items_per_page : ${requestScope['PAGE-INFO'].pageSize}, //每页显示数据数量,就是pageSize
current_page : ${requestScope['PAGE-INFO'].pageNum - 1},//当前页页码
prev_text : "上一页", //上一页文本
next_text : "下一页" //下一页文本
};
// 显示分页导航条
$("#Pagination").pagination(totalRecord, paginationProperties);
}
// 在每一次点击“上一页”、“下一页”、“页码”时执行这个函数跳转页面
function pageselectCallback(pageIndex, jq) {
// pageIndex从0开始,pageNum从1开始
var pageNum = pageIndex + 1;
// 跳转页面
window.location.href = "admin/query/for/search.html?pageNum="+pageNum;
return false;
}
4.9.4 Pagination修改源码
修改/atcrowdfunding-admin-1-webui/src/main/webapp/script/jquery.pagination.js文件,将158行注释掉
// opts.callback(current_page, this);
对应的问题:Pagination因为在这个地方重新加载页面,会造成死循环
4.10 关键词查询
4.10.1 将表单修改为可以提交的状态
- 所在工程:atcrowdfunding-admin-1-webui
- 所在页面:admin-page.jsp
4.10.2 跳转页面时携带keyword请求参数
// 在每一次点击“上一页”、“下一页”、“页码”时执行这个函数跳转页面
function pageselectCallback(pageIndex, jq) {
// pageIndex从0开始,pageNum从1开始
var pageNum = pageIndex + 1;
// 跳转页面
window.location.href = "admin/query/for/search.html?pageNum="+pageNum+"&keyword=${param.keyword}";
return false;
}
AdminMapper.xml
5. 批量删除
5.1 全选/全不选功能
5.1.1 标记要操作的多选框
#
账号
名称
邮箱地址
操作
抱歉!没有符合您要求的查询结果!
${myStatus.count }
${admin.loginAcct }
${admin.userName }
${admin.email }
5.1.2 jQuery代码
// 全选/全不选功能
$("#summaryBox").click(function() {
// 获取当前#summaryBox的勾选状态
// this代表当前多选框对象(DOM对象)
// checked属性为true时表示被勾选,为false时表示没有被勾选
// 使用checkStatus设置.itemBox的状态
$(".itemBox").prop("checked",this.checked);
});
5.2 批量删除按钮绑定单击响应函数
5.2.1 给批量删除按钮标记id
5.2.2 给itemBox设置adminId属性
说明:adminId属性是HTML标签本身并没有的属性,是我们强行设置的。
5.3 封装统一的Ajax响应结果
所在工程:atcrowdfunding-admin-4-entity
全类名:com.rgh.crowd.funding.entity.ResultEntity
/**
* 统一整个项目中所有Ajax请求的响应格式,作为项目的一个开发规范
* @author Lenovo
*
*/
public class ResultEntity {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
public static final String NO_MESSAGE = "NO_MESSAGE";
public static final String NO_DATA = "NO_DATA";
// 方便返回成功结果(不携带查询结果情况)
public static ResultEntity successWithoutData() {
return new ResultEntity(SUCCESS, NO_MESSAGE, NO_DATA);
}
// 方便返回成功结果(携带查询结果情况)
public static ResultEntity successWithoutData(E data) {
return new ResultEntity(SUCCESS, NO_MESSAGE, data);
}
// 方便返回失败结果
public static ResultEntity failed(E data, String message) {
return new ResultEntity(FAILED, message, data);
}
private String result;
private String message;
private T data;
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
super();
this.result = result;
this.message = message;
this.data = data;
}
@Override
public String toString() {
return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
5.4 jackson和Spring版本兼容性问题
5.4.1 方案一【×】
- 和jackson-mapper-asl的1.9.2兼容的Spring版本是4.0.0。
- 但是Spring的4.0.0和我们要使用的SpringSecurity版本不兼容。
5.4.2 方案二【√】
- Spring使用5.0.2
- jackson-core使用2.9.8
- jackson-databind使用2.9.8
父工程pom.xml文件更改依赖
com.fasterxml.jackson.core
jackson-core
2.9.8
com.fasterxml.jackson.core
jackson-databind
2.9.8
子工程atcrowdfunding-admin-2-component的pom.xml文件
com.fasterxml.jackson.core
jackson-core
com.fasterxml.jackson.core
jackson-databind
5.5 勾选检查
当用户在页面上没有勾选任何多选框,然后点击批量删除按钮时应该给出提示并不执行删除。
5.6 删除前确认
弹出确认框,让用户确认是否删除。
5.7 删除完成善后
- 如果删除操作执行成功则跳转页面。
- 如果失败则弹出提示消息。
5.8 最终代码
5.8.1 前端JS部分
// 全选/全不选功能
$("#summaryBox").click(function() {
// 获取当前#summaryBox的勾选状态
// this代表当前多选框对象(DOM对象)
// checked属性为true时表示被勾选,为false时表示没有被勾选
// 使用checkStatus设置.itemBox的状态
$(".itemBox").prop("checked",this.checked);
});
// 给批量删除按钮绑定单击响应函数
$("#batchRemoveBtn").click(function(){
// 创建数组对象:存储adminId
var adminIdArray = new Array();
// 创建数组对象:存储loginAcct
var loginAcctArray = new Array();
// 通过jQuery选择器定位到所有被选中itemBox,然后遍历
$(".itemBox:checked").each(function(){
<%-- --%>
// this.adminId拿不到值,原因是:this作为DOM对象无法读取HTML标签本身没有的属性
// 将this转换为jQuery对象调用attr()函数就能够拿到值
var adminId = $(this).attr("adminId");
// 调用数组对象的push()方法将数据存入数组
adminIdArray.push(adminId);
// loginAcct
var loginAcct = $(this) // 当前checkbox对象
.parent("td") // 包含checkbox的td
.next() // 当前td的下一个兄弟元素,也就是下一个td
.text(); // 下一个td的标签内部的文本
loginAcctArray.push(loginAcct);
});
// 检查adminIdArray是否包含有效数据
if(adminIdArray.length == 0) {
// 给出提示
alert("请勾选您要删除的记录!");
// 函数停止执行
return ;
}
// 给出确认提示,让用户确认是否真的删除这两条记录
var confirmResult = confirm("您真的要删除"+loginAcctArray+"信息吗?操作不可逆,请谨慎决定!");
// 如果用户点击了取消,那么让函数停止执行
if(!confirmResult) {
return ;
}
// 将JSON数组转换为JSON字符串
// var a = [1,2,3,4,5]; 数组类型
// var b = "[1,2,3,4,5]"; 字符串类型
// var c = {"userName":"tom"}; 对象类型
// var d = "{\"userName\":\"tom\"}"; 字符串类型
var requestBody = JSON.stringify(adminIdArray);
// 发送Ajax请求将adminIdArray发送给handler方法
$.ajax({
"url":"admin/batch/remove.json", // 服务器端接收请求的URL地址
"type":"post", // 设置请求方式为POST
"contentType":"application/json;charset=UTF-8", // 设置请求体内容类型,告诉服务器当前请求体发送的是JSON数据
"data":requestBody, // 请求体真正要发送给服务器的数据
"dataType":"json", // 把服务器端返回的数据当作JSON格式解析
"success":function(response) { // 服务器处理请求成功后执行的函数,响应体以参数形式传入当前函数
console.log(response);
var result = response.result;
if(result == "SUCCESS") {
// 跳转页面
window.location.href = "admin/query/for/search.html?pageNum=${requestScope['PAGE-INFO'].pageNum}&keyword=${param.keyword}";
}
if(result == "FAILED") {
alert(response.message);
return ;
}
},
"error":function(response) { // 服务器处理请求失败后执行的函数,响应体以参数形式传入当前函数
alert(response.message);
return ;
}
});
});
5.8.2 后端部分
5.8.2.1 @RequestBody和@ResponseBody注解
- 所在工程:atcrowdfunding-admin-2-component
- 全类名:com.rgh.crowd.funding.handler.AdminHandler
//将当前handler方法的返回值作为响应体返回,不经过试图解析器
@ResponseBody
@RequestMapping("/admin/batch/remove")
public ResultEntity batchRemove(@RequestBody List adminIdList){
try {
adminService.batchRemove(adminIdList);
return ResultEntity.successWithoutData();
}catch (Exception e){
return ResultEntity.failed(null,e.getMessage());
}
}
- 所在工程:atcrowdfunding-admin-2-component
- 全类名:com.rgh.crowd.funding.service.impl.AdminServiceImpl
public void batchRemove(List adminIdList) {
//QBC:Query By Criteria
//创建AdminExample对象
AdminExample adminExample = new AdminExample();
//创建Criteria对象
//Criteria对象可以帮助我们封装查询条件
//通过使用Criteria对象,可以把Java代码转换成SQL语句中WHERE字句里面的具体查询条件
AdminExample.Criteria criteria = adminExample.createCriteria();
//针对要查询的字段封装具体的查询条件
criteria.andIdIn(adminIdList);
//执行具体操作时把封装了查询条件的Example对象传入
adminMapper.deleteByExample(adminExample);
}