重点内容
声名
提示:这里没有完整的项目流程,只记录了动态查询网页的重点内容。
如何搭建一个完整项目流程:基于 Spring Mvc 一个简单项目的基本流程 <用户注册功能>(校正版)
提示:以下是本篇文章正文内容,下面案例可供参考
一、后端
1. mapper(非重点)
1. 接口文件
定义了方法:
List<UserInfo> findAll();
2. 映射文件
编写了查询信息的SQL语句:
<select id="findAll" resultType="com.upc.oa.po.UserInfo">
select userId, userName, userRealName, userSex, userEmail
from user_info
where userStatus=1
</select>
3. 测试(略)
2. 业务逻辑(重点)
上次博客中提到过,业务逻辑存在的必要性即处理数据。这此我们分页查询数据,则从mapper获取的数据,将在业务逻辑中进行分页处理,在反馈给控制器时已经是分页处理后的数据了。
1. 配置PageHelper
在pom.xml文件空白处,右键选择Generate-Dependency-搜索pagehelper-选择pagehelper-spring-boot-starter 1.3.1,效果如下:
2. 创建接口
使用新的实体类PageInfo作为业务逻辑的返回值类型。这是PageHelper里一种数据类型,将原来的List结构体数据转化为PageInfo实体类后,可自动封装处理List数据,变成分页处理后的结果。在service接口文件中写:
package com.upc.oa.service;
import com.github.pagehelper.PageInfo;
import com.upc.oa.dto.UserinfoDto;
import com.upc.oa.po.UserInfo;
import java.util.List;
public interface UserInfoService {
// 返回值类型:PageInfo<>
// 参数是:pageNum页码和pageSize每页显示的总数据条数
PageInfo<UserInfo> findAll(int pageNum, int pageSize);
}
3. 创建impl
这里需要将mapper查询到的数据,封装为PageInfo类型,然后反馈给控制器
package com.upc.oa.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.upc.oa.dto.UserinfoDto;
import com.upc.oa.mapper.UserinfoMapper;
import com.upc.oa.po.UserInfo;
import com.upc.oa.service.UserInfoService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
// 注解,声名这是业务逻辑
@Service
public class UserInfoServiceImpl implements UserInfoService {
// 声名要对哪个映射文件进行操作
@Resource
private UserinfoMapper userinfoMapper;
@Override
public PageInfo<UserInfo> findAll(int pageNum, int pageSize) {
// 定义PageInfo<>数据类型的变量,封装mapper的查询结果
PageInfo<UserInfo> page=null;
// 设置显示页:显示页的页码是第pageNum页;显示页显示的条数pageSize条
PageHelper.startPage(pageNum,pageSize);
// 获取后端查询数据,保存在user里
List<UserInfo> users=null;
users=userinfoMapper.findAll();
// 将users文件转为PageInfo格式
// 4:将默认的每页显示8个数据,设置为每页显示4个数据
page=new PageInfo<>(users,4);
// pageSize 与 这个 4 的区别:如果没有传入pageSize则默认每页显示4条,反之每页显示pagesize条
// 返回处理后的查询结果
return page;
}
}
4. 测试
业务逻辑也是可以测试的。
- 创建测试文件,选择要测试的接口
- 添加注解,编写参数,测试业务逻辑即可
package com.upc.oa.service;
import com.github.pagehelper.PageInfo;
import com.upc.oa.po.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoServiceTest {
@Resource
private UserInfoService userInfoService;
@Test
void findAll() {
// 编写参数
int pageNum= 1;
int pageSize= 3;
// 调用接口函数
PageInfo<UserInfo> page = userInfoService.findAll(pageNum,pageSize);
// 输出测试结果
System.out.println(page);
}
}
3. 控制器(重点)
1. 创建控制器
注意控制器返回的数据类型也是PageInfo
package com.upc.oa.controller;
import com.github.pagehelper.PageInfo;
import com.upc.oa.dto.UserinfoDto;
import com.upc.oa.po.UserInfo;
import com.upc.oa.service.UserInfoService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
// 配置路径 /user
@RestController
@RequestMapping("/user")
public class UserController {
// 声名控制器要调用业务逻辑是哪个
@Resource
private UserInfoService userInfoService;
// 在 @RestController的声名下,会将我们返回的数据自动转为json格式,可以直接使用
// 控制器向前端返回的数据:为 PageInfo<UserInfo> 封装的查询结果,
// 前端向控制器传入的参数为:int pageNum, int pageSize
@RequestMapping("/findAll")
public PageInfo<UserInfo> findAll(int pageNum, int pageSize){
// 调用业务逻辑,将业务逻辑反馈的数据直接返回即可
return userInfoService.findAll(pageNum,pageSize);
}
}
2. 测试控制器
- 启动XXXApplicaton
一定要启动成功,正常运行才可进入后续步骤 - 选择Idea自己的插件
界面介绍:
- 配置界面
- 测试
二、前端(重点)
先在static文件夹下,创建users.html文件
1. 配置jQuery、bootstrap
bootstrap: 提供大量的Css样式和少量Js组键。Css中有一种选择器叫类选择器,我们定义类名,然后编写这个类选择器的修饰内容,我们需要修饰谁,就在谁那里添加class=“类名”。而bootStrap是自己定义好了一堆Css的类选择器函数,我们自己直接在控件中添加自己想要的class=""即可进行修饰。
使用方法: 访问官网,使用模板套用即可BootStrap模板
1.1 搭载jquery
在static文件夹下,创建jquery文件夹,将jquery-3.6.0.min.js复制到该文件夹下
2. 搭载bootstrap
- 复制bootstrap文件夹
- 粘贴到static中,目录结构如下即可
3. 配置jQuery、bootstrap
采用拖拽方法将需要的文件直接拖拽到title标签下方空白处,注意拖拽顺序!
4. ReBuild 项目
2. 编写Html文件:
1. 触发事件
全选功能为例:
Html部分
<!-- 表头部分 -->
<thead>
<tr>
<!-- 全选功能 :点击全选小方框,所有显示数据都被勾选 -->
<td>
<!-- onclik="selectAllCk" :设置触发事件,当选中这个方框时会触发selectAllCk事件,
事件具体内容可以在Script标签中编写;
this.checked:若该方框被选中为True,反之为False;
事件输入的参数:this.checked,如果该方框被选中则selectAllCk事件的参数为True;
-->
<input type="checkbox" id="chall" onclick="selectAllCk(this.checked)">
</td>
<td>账号</td>
<td>真实姓名</td>
<td>性别</td>
<td>操作</td>
</tr>
</thead>
Script部分
<!-- 编写selectAllCk的事件 (传入的参数命名为f)-->
<script>
function selectAllCk(f){
//实现全选原理:若全选选框被选中,则传入的参数f为ture,则所有name为ids的选框都变为True被选中
//找界面中input控件中所有name为ids的控件,并把控件的checked设置为传入的参数f
jQuery("input[name='ids']").prop("checked",f);
// 使用name命名数据勾选框的原因:
// name属性值可以重复,id就是主键不可重复,对于全选功能,使用name属性,可以同时勾选多个name为ids的input
}
</script>
2. 使用jQuery传递数据/语句
在Script脚本中,反引号中可以编写Html语句,使用jQuery传递到对应控件即可执行
在表体显示数据为例:
Html部分
<!-- 表体部分,具体内容靠script的方法传入 -->
<tbody id="data">
</tbody>
Script部分
// 1.获取查询的数据
var arrs=rst.list;
var trs = '';
// 利用循环+反引号,获取了多少数据,就创建多少条语句
for (var i=0;i<arrs.length;i++){
//获取每一行对应的用户对象的信息
var u=arrs[i];
// 创建语句
trs=trs+`
<tr>
<td>
<input type="checkbox" name="ids" value="${u.userId}">
</td>
<td>${u.userName}</td>
<td>${u.userRealName}</td>
<td>${u.userSex==1?'男':'女'}</td>
<td>${u.userEmail}</td>
<td>
<!-- 按钮,采用超链接 -->
<a href="#" >删除</a>
<a href="#" >编辑</a>
</td>
</tr>
`;
}
// 把处理trs代码字符串添加到Html部分中,id为data的控件中
jQuery("#data").html(trs)
3. 知识点
1. script与html的数据交互
- 在html设置触发事件,html可向脚本传递参数,而事件内容可修改html
- 在script,使用jQuery,获取对应id/name的数值
- 在script,通过jquery,定位id,可向html传递数据、甚至是html语句
4.完整的Html
完成了分页显示功能,里面html与script交织在一起,实在是没有找到一个好的方法来表示,只能把完整代码和注释放在这了,累了,毁灭吧。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 配置jQuery、bootStrap -->
<link rel="stylesheet" href="bootstrap-4.6.0-dist/css/bootstrap.min.css">
<script src="jquery/jquery-3.6.0.min.js"></script>
<script src="bootstrap-4.6.0-dist/js/bootstrap.min.js"></script>
<!-- Css部分,对部分控件进行修饰 -->
<style type="text/css">
/*给按钮换个位置,放右边,设置一下据其他控件的距离*/
#top{
/**/
padding: 10px 20px;
text-align: right;
}
/*让两个div控件在一行显示的修饰方法*/
/*让page_div里的right向右飘*/
#page_div #right{
float: right;
}
/*让page_div里的left向左飘*/
#page_div #left{
float: left;
}
</style>
</head>
<body>
<!--整个界面放在该div中-->
<div id="container">
<!-- 批量删除按钮-->
<div id="top">
<button type="button" class="btn btn-danger btn-lg">批量删除</button>
</div>
<!-- 显示数据的部分 -->
<div id="bottom">
<!-- 将整个显示数据的部分设置为一个表格,分为:表头thead、表体tbody、表尾tfoot -->
<table class="table table-bordered table-hover table-striped">
<!-- 表头部分 -->
<thead>
<tr>
<!-- 全选功能 :点击全选小方框,所有显示数据都被勾选 -->
<td>
<!-- onclik="selectAllCk" :设置触发事件,当选中这个方框时会触发selectAllCk事件,
事件具体内容可以在Script标签中编写;
this.checked:若该方框被选中为True,反之为False;
事件输入的参数:this.checked,如果该方框被选中则selectAllCk事件的参数为True;
-->
<input type="checkbox" id="chall" onclick="selectAllCk(this.checked)">
</td>
<td>账号</td>
<td>真实姓名</td>
<td>性别</td>
<td>操作</td>
</tr>
</thead>
<!-- 表体部分,具体内容靠script的方法传入 -->
<tbody id="data">
</tbody>
<!-- 表尾部分 -->
<tfoot>
<tr>
<!-- 分页显示 colspan=6:即设置一行中单个列宽,这里设置单个列宽为原来的6倍,即一个单元格占六列宽-->
<td colspan="6">
<div id="page_div">
<!-- 左边部分 分页显示情况 -->
<div id="left">
<!-- span是行内标签,特点是:等于是一行里有多个还不自动换行 -->
当前页/总页数: <span id="pageNum"></span> / <span id="pages"></span> 总条数=<span id="totals"></span>
</div>
<!-- 右边部分 换页操作 -->
<div id="right">
</div>
</div>
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- 编写selectAllCk的事件 (传入的参数命名为f)-->
<script>
function selectAllCk(f){
//实现全选原理:若全选选框被选中,则传入的参数f为ture,则所有name为ids的选框都变为True被选中
//找界面中input控件中所有name为ids的控件,并把控件的checked设置为传入的参数f
jQuery("input[name='ids']").prop("checked",f);
// 使用name命名数据勾选框的原因:
// name属性值可以重复,id就是主键不可重复,对于全选功能,使用name属性,可以同时勾选多个name为ids的input
}
</script>
<!--页面加载时,完成获取某一页的数据-->
<script>
//定义全局变量,存放:当前页码、每页条数、总页数
var pageNum = 1; // 默认显示第一页
var pageSize = 3; // 默认每页显示3条
var pageNums = 0 ; // 默认总页数为 0
// init():每次调用init方法时,会获取后台数据,重新显示表体、表尾
function init() {
// 获取后台数据部分
var param={
//控制器参数 定义变量
"pageNum" : pageNum,
"pageSize" : pageSize
};
//使用jquery访问后端的user/findAll控制器
// rst 是控制器反馈的查询结果
jQuery.post("user/findAll",param,function (rst) {
// 表尾的left:分页情况------------------------------
// 获取总页数
pageNums=rst.pages;
//将pageNums数值传递到id=pages的控件中,下面同理
jQuery("#pages").html(pageNums);
jQuery("#pageNum").html(pageNum);
jQuery("#totals").html(rst.total);
//--------------------------------------------------
//表体:显示查询数据----------------------------------
// 1.获取查询的数据
var arrs=rst.list;
var trs = '';
// 利用循环,我们获取了多少数据,就创建多少行对应语句
for (var i=0;i<arrs.length;i++){
//循环一次,在页面中产生一行数据
//获取每一行对应的用户对象
var u=arrs[i];
//使用反引号,作用:定义字符串模板
trs=trs+`
<tr>
<td>
<input type="checkbox" name="ids" value="${u.userId}">
</td>
<td>${u.userName}</td>
<td>${u.userRealName}</td>
<td>${u.userSex==1?'男':'女'}</td>
<td>${u.userEmail}</td>
<td>
<!-- 按钮,采用超链接 -->
<a href="#" >删除</a>
<a href="#" >编辑</a>
</td>
</tr>
`;
}
// 把for循环处理好的html代码字符串添加到data表体中去
jQuery("#data").html(trs)
//------------------------------------------------------
//设置表尾right:换页部分--------------------------------
// 上一页按钮---------
var nav=`
<ul class="pagination">
<li class="page-item">
<!-- 设置触发事件,当点击上一页,触发pageUtil事件,传入要前往的页(当前页的上一页) -->
<a href="#" class="page-link" οnclick="pageUtil(${pageNum-1})">上一页</a>
</li>
`;
//循环设置页码,以及激活页码-------------
//rst.navigatepageNums:是个数组,例如:若是1~4页,则数组是[1,2,3,4]
var nums=rst.navigatepageNums;
for(var i=0;i<nums.length;i++)
{
nav+=` <!-- 如果当前页与页码数组中的某一页相等则,激活当前页 -->
<li class="page-item" ${pageNum==nums[i]?'active':''}>
<a href="#" class="page-link">${nums[i]}</a>
</li>
`;
}
// 下一页按钮----------------
nav=nav+`
<li class="page-item">
<!-- 设置触发事件,当点击下一页,触发pageUtil事件,传入要前往的页(当前页的下一页) -->
<a href="#" class="page-link" οnclick="pageUtil(${pageNum+1})">下一页</a>
</li>
</ul>
`;
// 将换页设置传到right控件
jQuery("#right").html(nav);
})
}
//页面加载完毕后,重新执行init方法,此时若用户选择了新的页码,则刷新数据,反之数据还是原来数据,只是又获取了一次
jQuery(function () {
init();
})
</script>
<!--编写pageUtil事件-->
<script>
//完成换页操作
function pageUtil(num) {
//修改pageNum为num,即要前往的页
pageNum=num;
// 如果进入当前页小于1就保持第一页
if(num<=1)
{
pageNum=1;
}
// 如果进入当前页大于,总页数就直显示最后一页
if(num>pageNums){
pageNum=pageNums;
}
// 用户每次修改页码后,我们重新调用init()函数,即可重新显示对应页的数据(pageNum是个public属性,可以直接调用、修改)
init();
}
</script>
</body>
</html>
最终效果,除了删除功能没有实现,其他都是实现了,等我功力深了,再来填坑。