1. 修正
1.1 登錄完成跳轉(zhuǎn)到后臺主頁面改成重定向
@RequestMapping("/admin/do/login")
public String doLogin(@RequestParam("loginAcct") String loginAcct,
@RequestParam("userPswd") String userPswd,
Model model,
HttpSession session){
//調(diào)用adminService的login方法執(zhí)行登陸業(yè)務(wù)邏輯夭问,返回查詢到的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
需要調(diào)整的內(nèi)容:
- 字符集改成UTF-8
- 加入title和base標簽
- 把“張三”改成${sessionScope['LOGIN-ADMIN'].userName }
- 把退出的超鏈接改成實際地址
<a href="admin/logout.html"><i class="glyphicon glyphicon-off"></i> 退出系統(tǒng)</a>
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 創(chuàng)建JSP模版
2.3.1 模版內(nèi)容
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="UTF-8">
<%@ include file="/WEB-INF/include-head.jsp" %>
<body>
<%@ include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@ include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
</div>
</div>
</div>
</body>
</html>
2.3.2 創(chuàng)建JSP模版操作
3. Admin維護功能清單
- 分頁顯示全部Admin數(shù)據(jù)√
- 分頁顯示Admin數(shù)據(jù)的關(guān)鍵詞查詢結(jié)果√
- 批量刪除
- 單條刪除
- 新增Admin
- 更新Admin
分頁顯示全部數(shù)據(jù)和查詢結(jié)果在后端可以合并為同一個操作评甜。
批量刪除和單條刪除在后端可以合并為同一個操作忍坷。
4. 分頁功能
4.1 分析
4.2 執(zhí)行查詢的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 作用
以完全非侵入的方式在原有查詢基礎(chǔ)上附加分頁效果佩研。從SQL層面來說旬薯,在SQL語句后面附加LIMIT子句绊序。從Java代碼來說骤公,把原來返回的List類型封裝為Page類型淋样。
4.3.2 依賴信息
<!-- 父工程 -->
<!-- MyBatis分頁插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
<!-- 子工程 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
4.3.3 配置方式
- 所在工程:atcrowdfunding-admin-1-webui
- 配置文件:spring-persist-mybatis.xml
- 在SqlSessionFactoryBean中配置MyBatis插件
<!-- 配置PageHelper插件 -->
<property name="plugins">
<array>
<!--配置PageHelper全類名-->
<bean class="com.github.pagehelper.PageHelper">
<!--配置相關(guān)參數(shù)-->
<property name="properties">
<props>
<!--數(shù)據(jù)庫方言指定數(shù)據(jù)庫類型為MySQL-->
<prop key="dialect">mysql</prop>
<!--配置自動修正頁碼-->
<!--pageNo的有效范圍:1~總頁數(shù)-->
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</array>
</property>
4.4 AdminMapper
4.4.1 Mapper配置文件
- 所在工程:atcrowdfunding-admin-1-webui
- 文件:resources/mybatis/mapper/AdminMapper.xml
<select id="selectAdminListByKeyword" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_admin
WHERE
login_acct LIKE CONCAT("%", #{keyword}, "%")
OR user_name LIKE CONCAT("%", #{keyword}, "%")
OR email LIKE CONCAT("%", #{keyword}, "%")
</select>
4.4.2 Mapper接口
- 所在工程:atcrowdfunding-admin-2-component
- 全類名:com.rgh.crowd.funding.mapper.AdminMapper
List<Admin> selectAdminListByKeyword(String keyword);
4.5 AdminService
所在工程:atcrowdfunding-admin-2-component
接口方法:
PageInfo<Admin> queryForKeywordSearch(Integer pageNum, Integer pageSize, String keyword);
實現(xiàn)類方法:
@Override
public PageInfo<Admin> queryForKeywordSearch(Integer pageNum, Integer pageSize, String keyword) {
// 1.調(diào)用PageHelper的工具方法彪见,開啟分頁功能
PageHelper.startPage(pageNum, pageSize);
// 2.執(zhí)行分頁查詢
List<Admin> 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(
// 如果頁面上沒有提供對應(yīng)的請求參數(shù)捕犬,那么可以使用defaultValue指定默認值
@RequestParam(value="pageNum", defaultValue="1") Integer pageNum,
@RequestParam(value="pageSize", defaultValue="5") Integer pageSize,
@RequestParam(value="keyword", defaultValue="") String keyword,
Model model) {
PageInfo<Admin> pageInfo = adminService.queryForKeywordSearch(pageNum, pageSize, keyword);
model.addAttribute(CrowdFundingConstant.ATTR_NAME_PAGE_INFO, pageInfo);
return "admin-page";
}
4.7 插入測試用的假數(shù)據(jù)
- 所在工程: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 頁面顯示:主體部分
<tbody>
<c:if test="${empty requestScope['PAGE-INFO'].list }">
<tr>
<td style="text-align: center;" colspan="6">抱歉碉碉!沒有符合您要求的查詢結(jié)果垢粮!</td>
</tr>
</c:if>
<c:if test="${!empty requestScope['PAGE-INFO'].list }">
<c:forEach items="${requestScope['PAGE-INFO'].list }" var="admin" varStatus="myStatus">
<tr>
<td>${myStatus.count }</td>
<td><input type="checkbox"></td>
<td>${admin.loginAcct }</td>
<td>${admin.userName }</td>
<td>${admin.email }</td>
<td>
<button type="button" class="btn btn-success btn-xs">
<i class=" glyphicon glyphicon-check"></i>
</button>
<button type="button" class="btn btn-primary btn-xs">
<i class=" glyphicon glyphicon-pencil"></i>
</button>
<button type="button" class="btn btn-danger btn-xs">
<i class=" glyphicon glyphicon-remove"></i>
</button>
</td>
</tr>
</c:forEach>
</c:if>
</tbody>
4.9 頁面顯示:導(dǎo)航條部分
使用一個基于jQuery的分頁插件:Pagination
4.9.1 環(huán)境搭建
-
加入樣式文件
將pagination.css復(fù)制到/atcrowdfunding-admin-1-webui/src/main/webapp/css目錄下
-
在需要的頁面引用pagination.css
在admin-page.jsp中引入
<link rel="stylesheet" href="css/pagination.css"/>
-
加入Pagination的js文件
將jquery.pagination.js復(fù)制到/atcrowdfunding-admin-1-webui/src/main/webapp/script目錄下
-
在需要的頁面引用jquery.pagination.js
<script type="text/javascript" src="script/jquery.pagination.js"></script>
4.9.2 分頁導(dǎo)航條需要在HTML標簽中加入的部分
<tfoot>
<tr>
<td colspan="6" align="center">
<div id="Pagination" class="pagination"><!-- 這里顯示分頁 --></div>
</td>
</tr>
</tfoot>
4.9.3 jQuery代碼
$(function() {
// 對分頁導(dǎo)航條顯示進行初始化
initPagination();
});
// 聲明函數(shù)封裝導(dǎo)航條初始化操作
function initPagination() {
// 聲明變量存儲總記錄數(shù)
var totalRecord = ${requestScope['PAGE-INFO'].total};
// 聲明變量存儲分頁導(dǎo)航條顯示時的屬性設(shè)置
var paginationProperties = {
num_edge_entries : 3, //邊緣頁數(shù)
num_display_entries : 5, //主體頁數(shù)
callback : pageselectCallback, //回調(diào)函數(shù)
items_per_page : ${requestScope['PAGE-INFO'].pageSize}, //每頁顯示數(shù)據(jù)數(shù)量昔善,就是pageSize
current_page : ${requestScope['PAGE-INFO'].pageNum - 1},//當前頁頁碼
prev_text : "上一頁", //上一頁文本
next_text : "下一頁" //下一頁文本
};
// 顯示分頁導(dǎo)航條
$("#Pagination").pagination(totalRecord, paginationProperties);
}
// 在每一次點擊“上一頁”君仆、“下一頁”袖订、“頁碼”時執(zhí)行這個函數(shù)跳轉(zhuǎn)頁面
function pageselectCallback(pageIndex, jq) {
// pageIndex從0開始洛姑,pageNum從1開始
var pageNum = pageIndex + 1;
// 跳轉(zhuǎn)頁面
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);
對應(yīng)的問題:Pagination因為在這個地方重新加載頁面蕴侧,會造成死循環(huán)
4.10 關(guān)鍵詞查詢
4.10.1 將表單修改為可以提交的狀態(tài)
- 所在工程:atcrowdfunding-admin-1-webui
- 所在頁面:admin-page.jsp
<form action="admin/query/for/search.html" class="form-inline" role="form" style="float:left;" method="post">
<div class="form-group has-feedback">
<div class="input-group">
<div class="input-group-addon">查詢條件</div>
<input class="form-control has-success"
type="text"
placeholder="請輸入查詢條件"
name="keyword"
>
</div>
</div>
<button type="submit" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i> 查詢
</button>
</form>
4.10.2 跳轉(zhuǎn)頁面時攜帶keyword請求參數(shù)
// 在每一次點擊“上一頁”净宵、“下一頁”择葡、“頁碼”時執(zhí)行這個函數(shù)跳轉(zhuǎn)頁面
function pageselectCallback(pageIndex, jq) {
// pageIndex從0開始敏储,pageNum從1開始
var pageNum = pageIndex + 1;
// 跳轉(zhuǎn)頁面
window.location.href = "admin/query/for/search.html?pageNum="+pageNum+"&keyword=${param.keyword}";
return false;
}
AdminMapper.xml
<select id="selectAdminListByKeyword" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
from t_admin
where
login_acct like concat("%",#{keyword},"%")
OR user_name LIKE concat("%",#{keyword},"%")
OR email LIKE concat("%",#{keyword},"%")
order by id desc
</select>
5. 批量刪除
5.1 全選/全不選功能
5.1.1 標記要操作的多選框
<thead>
<tr>
<th width="30">#</th>
<th width="30"><input id="summaryBox" type="checkbox"></th>
<th>賬號</th>
<th>名稱</th>
<th>郵箱地址</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody>
<c:if test="${empty requestScope['PAGE-INFO'].list }">
<tr>
<td style="text-align: center;" colspan="6">抱歉!沒有符合您要求的查詢結(jié)果畦幢!</td>
</tr>
</c:if>
<c:if test="${!empty requestScope['PAGE-INFO'].list }">
<c:forEach items="${requestScope['PAGE-INFO'].list }"
var="admin" varStatus="myStatus">
<tr>
<td>${myStatus.count }</td>
<td><input class="itemBox" type="checkbox"></td>
<td>${admin.loginAcct }</td>
<td>${admin.userName }</td>
<td>${admin.email }</td>
<td>
<button type="button" class="btn btn-success btn-xs">
<i class=" glyphicon glyphicon-check"></i>
</button>
<button type="button" class="btn btn-primary btn-xs">
<i class=" glyphicon glyphicon-pencil"></i>
</button>
<button type="button" class="btn btn-danger btn-xs">
<i class=" glyphicon glyphicon-remove"></i>
</button>
</td>
</tr>
</c:forEach>
</c:if>
</tbody>
5.1.2 jQuery代碼
// 全選/全不選功能
$("#summaryBox").click(function() {
// 獲取當前#summaryBox的勾選狀態(tài)
// this代表當前多選框?qū)ο螅―OM對象)
// checked屬性為true時表示被勾選,為false時表示沒有被勾選
// 使用checkStatus設(shè)置.itemBox的狀態(tài)
$(".itemBox").prop("checked",this.checked);
});
5.2 批量刪除按鈕綁定單擊響應(yīng)函數(shù)
5.2.1 給批量刪除按鈕標記id
<button
id="batchRemoveBtn"
type="button"
class="btn btn-danger"
style="float: right; margin-left: 10px;">
<i class=" glyphicon glyphicon-remove"></i> 刪除
</button>
5.2.2 給itemBox設(shè)置adminId屬性
<input adminId="${admin.id }" class="itemBox" type="checkbox">
說明:adminId屬性是HTML標簽本身并沒有的屬性贝搁,是我們強行設(shè)置的雷逆。
5.3 封裝統(tǒng)一的Ajax響應(yīng)結(jié)果
所在工程:atcrowdfunding-admin-4-entity
全類名:com.rgh.crowd.funding.entity.ResultEntity
/**
* 統(tǒng)一整個項目中所有Ajax請求的響應(yīng)格式膀哲,作為項目的一個開發(fā)規(guī)范
* @author Lenovo
*
*/
public class ResultEntity<T> {
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";
// 方便返回成功結(jié)果(不攜帶查詢結(jié)果情況)
public static ResultEntity<String> successWithoutData() {
return new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
}
// 方便返回成功結(jié)果(攜帶查詢結(jié)果情況)
public static <E> ResultEntity<E> successWithoutData(E data) {
return new ResultEntity<E>(SUCCESS, NO_MESSAGE, data);
}
// 方便返回失敗結(jié)果
public static <E> ResultEntity<E> failed(E data, String message) {
return new ResultEntity<E>(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文件更改依賴
<!-- Spring進行JSON數(shù)據(jù)轉(zhuǎn)換依賴 -->
<!--<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.2</version>
</dependency>-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
子工程atcrowdfunding-admin-2-component的pom.xml文件
<!--<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
5.5 勾選檢查
當用戶在頁面上沒有勾選任何多選框酱酬,然后點擊批量刪除按鈕時應(yīng)該給出提示并不執(zhí)行刪除膳沽。
5.6 刪除前確認
彈出確認框,讓用戶確認是否刪除陨界。
5.7 刪除完成善后
- 如果刪除操作執(zhí)行成功則跳轉(zhuǎn)頁面故俐。
- 如果失敗則彈出提示消息撕阎。
5.8 最終代碼
5.8.1 前端JS部分
// 全選/全不選功能
$("#summaryBox").click(function() {
// 獲取當前#summaryBox的勾選狀態(tài)
// this代表當前多選框?qū)ο螅―OM對象)
// checked屬性為true時表示被勾選动猬,為false時表示沒有被勾選
// 使用checkStatus設(shè)置.itemBox的狀態(tài)
$(".itemBox").prop("checked",this.checked);
});
// 給批量刪除按鈕綁定單擊響應(yīng)函數(shù)
$("#batchRemoveBtn").click(function(){
// 創(chuàng)建數(shù)組對象:存儲adminId
var adminIdArray = new Array();
// 創(chuàng)建數(shù)組對象:存儲loginAcct
var loginAcctArray = new Array();
// 通過jQuery選擇器定位到所有被選中itemBox赁咙,然后遍歷
$(".itemBox:checked").each(function(){
<%-- <input adminId="${admin.id }" class="itemBox" type="checkbox"> --%>
// this.adminId拿不到值彼水,原因是:this作為DOM對象無法讀取HTML標簽本身沒有的屬性
// 將this轉(zhuǎn)換為jQuery對象調(diào)用attr()函數(shù)就能夠拿到值
var adminId = $(this).attr("adminId");
// 調(diào)用數(shù)組對象的push()方法將數(shù)據(jù)存入數(shù)組
adminIdArray.push(adminId);
// <td><input type="checkbox" /></td><td>loginAcct</td>
var loginAcct = $(this) // 當前checkbox對象
.parent("td") // 包含checkbox的td
.next() // 當前td的下一個兄弟元素凤覆,也就是下一個td
.text(); // 下一個td的標簽內(nèi)部的文本
loginAcctArray.push(loginAcct);
});
// 檢查adminIdArray是否包含有效數(shù)據(jù)
if(adminIdArray.length == 0) {
// 給出提示
alert("請勾選您要刪除的記錄拆魏!");
// 函數(shù)停止執(zhí)行
return ;
}
// 給出確認提示盯桦,讓用戶確認是否真的刪除這兩條記錄
var confirmResult = confirm("您真的要刪除"+loginAcctArray+"信息嗎?操作不可逆渤刃,請謹慎決定拥峦!");
// 如果用戶點擊了取消,那么讓函數(shù)停止執(zhí)行
if(!confirmResult) {
return ;
}
// 將JSON數(shù)組轉(zhuǎn)換為JSON字符串
// var a = [1,2,3,4,5]; 數(shù)組類型
// var b = "[1,2,3,4,5]"; 字符串類型
// var c = {"userName":"tom"}; 對象類型
// var d = "{\"userName\":\"tom\"}"; 字符串類型
var requestBody = JSON.stringify(adminIdArray);
// 發(fā)送Ajax請求將adminIdArray發(fā)送給handler方法
$.ajax({
"url":"admin/batch/remove.json", // 服務(wù)器端接收請求的URL地址
"type":"post", // 設(shè)置請求方式為POST
"contentType":"application/json;charset=UTF-8", // 設(shè)置請求體內(nèi)容類型卖子,告訴服務(wù)器當前請求體發(fā)送的是JSON數(shù)據(jù)
"data":requestBody, // 請求體真正要發(fā)送給服務(wù)器的數(shù)據(jù)
"dataType":"json", // 把服務(wù)器端返回的數(shù)據(jù)當作JSON格式解析
"success":function(response) { // 服務(wù)器處理請求成功后執(zhí)行的函數(shù)略号,響應(yīng)體以參數(shù)形式傳入當前函數(shù)
console.log(response);
var result = response.result;
if(result == "SUCCESS") {
// 跳轉(zhuǎn)頁面
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) { // 服務(wù)器處理請求失敗后執(zhí)行的函數(shù)揪胃,響應(yīng)體以參數(shù)形式傳入當前函數(shù)
alert(response.message);
return ;
}
});
});
5.8.2 后端部分
5.8.2.1 @RequestBody和@ResponseBody注解
- 所在工程:atcrowdfunding-admin-2-component
- 全類名:com.rgh.crowd.funding.handler.AdminHandler
//將當前handler方法的返回值作為響應(yīng)體返回璃哟,不經(jīng)過試圖解析器
@ResponseBody
@RequestMapping("/admin/batch/remove")
public ResultEntity<String> batchRemove(@RequestBody List<Integer> 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<Integer> adminIdList) {
//QBC:Query By Criteria
//創(chuàng)建AdminExample對象
AdminExample adminExample = new AdminExample();
//創(chuàng)建Criteria對象
//Criteria對象可以幫助我們封裝查詢條件
//通過使用Criteria對象,可以把Java代碼轉(zhuǎn)換成SQL語句中WHERE字句里面的具體查詢條件
AdminExample.Criteria criteria = adminExample.createCriteria();
//針對要查詢的字段封裝具體的查詢條件
criteria.andIdIn(adminIdList);
//執(zhí)行具體操作時把封裝了查詢條件的Example對象傳入
adminMapper.deleteByExample(adminExample);
}