java每日精进 5.15【分页实现】
一、前端分页实现
1.1 Vue 界面
前端使用 Element UI 的 Pagination 组件 实现分页,结合搜索功能,展示租户列表。代码位于 tenant/index.vue,以下是核心部分的分析和注释。
<template><!-- 搜索工作栏 --><el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="租户名" prop="name"><el-input v-model="queryParams.name" placeholder="请输入租户名" clearable @keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="联系人" prop="contactName"><el-input v-model="queryParams.contactName" placeholder="请输入联系人" clearable @keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="联系手机" prop="contactMobile"><el-input v-model="queryParams.contactMobile" placeholder="请输入联系手机" clearable @keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="租户状态" prop="status"><el-select v-model="queryParams.status" placeholder="请选择租户状态" clearable><el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)":key="dict.value" :label="dict.label" :value="dict.value"/></el-select></el-form-item><el-form-item><el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button><el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><!-- 列表 --><el-table v-loading="loading" :data="list"><!-- 省略每一列... --></el-table><!-- 分页组件 --><pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" @pagination="getList"/>
</template>
- 搜索工作栏:
- 使用 <el-form> 创建搜索表单,绑定 queryParams 对象,包含分页和搜索参数(如 name、contactName、contactMobile、status)。
- 每个 <el-form-item> 对应一个搜索条件:
- name、contactName、contactMobile 使用 <el-input>,支持模糊查询。
- status 使用 <el-select>,从字典 DICT_TYPE.COMMON_STATUS 获取状态选项(如 0=正常,1=停用)。
- 搜索按钮触发 handleQuery,重置按钮触发 resetQuery。
- 列表展示:
- <el-table> 绑定 list 数据,显示租户列表,v-loading 控制加载状态。
- 分页组件:
- <pagination> 是 Element UI 的分页组件,显示当 total > 0 时。
- 属性:
- :total:总记录数,从后端返回。
- :page.sync:绑定当前页码 queryParams.pageNo,双向同步。
- :limit.sync:绑定每页条数 queryParams.pageSize,双向同步。
- @pagination:页码或条数变化时触发 getList,重新查询数据。
JavaScript 逻辑
<script>
import { getTenantPage } from "@/api/system/tenant";export default {name: "Tenant",components: {},data() {return {loading: true, // 控制表格加载状态showSearch: true, // 控制搜索栏显示total: 0, // 总记录数list: [], // 租户列表数据queryParams: { // 查询参数pageNo: 1, // 页码,默认为 1pageSize: 10, // 每页条数,默认为 10name: null, // 租户名contactName: null, // 联系人contactMobile: null, // 联系手机status: undefined // 租户状态}}},created() {this.getList(); // 页面初始化时查询数据},methods: {/** 查询列表 */getList() {this.loading = true; // 显示加载状态let params = {...this.queryParams}; // 复制查询参数,避免直接修改getTenantPage(params).then(response => {this.list = response.data.list; // 更新列表数据this.total = response.data.total; // 更新总记录数this.loading = false; // 关闭加载状态});},/** 搜索按钮操作 */handleQuery() {this.queryParams.pageNo = 1; // 重置为第一页this.getList(); // 执行查询},/** 重置按钮操作 */resetQuery() {this.resetForm("queryForm"); // 重置表单this.handleQuery(); // 重新查询}}
}
</script>
- 数据初始化:
- queryParams 包含分页参数(pageNo、pageSize)和搜索条件。
- total 和 list 用于存储后端返回的分页数据。
- 查询逻辑:
- getList:调用 getTenantPage API,传递 queryParams,更新 list 和 total。
- handleQuery:点击搜索按钮,重置 pageNo 为 1,调用 getList。
- resetQuery:重置表单,重置后重新查询。
- 加载状态:loading 控制表格的加载动画,提升用户体验。
1.2 API 请求
前端通过 system/tenant.js 定义 API 请求,调用后端分页接口。
import request from '@/utils/request'// 获得租户分页
export function getTenantPage(query) {return request({url: '/system/tenant/page',method: 'get',params: query})
}
- 功能:发送 GET 请求到 /system/tenant/page,携带 query 参数(包括 pageNo、pageSize 和搜索条件)。
- 实现:
- request 是基于 Axios 的封装,处理 HTTP 请求。
- params: query 将 queryParams 作为查询参数附加到 URL(如 /system/tenant/page?pageNo=1&pageSize=10&name=芋道)。
- 响应:期待后端返回 CommonResult<PageResult<TenantRespVO>>,包含 list(租户列表)和 total(总记录数)。
二、后端分页实现
2.1 Controller 接口
后端使用 MyBatis Plus 的分页功能,结合二次封装的 PageResult 类实现分页。TenantController 定义了分页接口。
@Tag(name = "管理后台 - 租户")
@RestController
@RequestMapping("/system/tenant")
public class TenantController {@Resourceprivate TenantService tenantService;@GetMapping("/page")@Operation(summary = "获得租户分页")@PreAuthorize("@ss.hasPermission('system:tenant:query')") // 权限校验public CommonResult<PageResult<TenantRespVO>> getTenantPage(@Valid TenantPageReqVO pageVO) {PageResult<TenantDO> pageResult = tenantService.getTenantPage(pageVO); // 调用 Servicereturn success(TenantConvert.INSTANCE.convertPage(pageResult)); // 转换并返回}
}
- 功能:处理 /system/tenant/page GET 请求,返回租户分页数据。
- 参数:
- @Valid TenantPageReqVO pageVO:分页和搜索参数,经过校验。
- 逻辑:
- 调用 tenantService.getTenantPage 获取 PageResult<TenantDO>。
- 使用 TenantConvert 将 TenantDO 转换为 TenantRespVO。
- 封装为 CommonResult 返回。
- 权限:@PreAuthorize 确保用户有 system:tenant:query 权限。
分页参数 PageParam
分页请求继承 PageParam 类,定义页码和每页条数。
@Schema(description = "分页参数")
@Data
public class PageParam implements Serializable {private static final Integer PAGE_NO = 1; // 默认页码private static final Integer PAGE_SIZE = 10; // 默认每页条数@Schema(description = "页码,从 1 开始", required = true, example = "1")@NotNull(message = "页码不能为空")@Min(value = 1, message = "页码最小值为 1")private Integer pageNo = PAGE_NO;@Schema(description = "每页条数,最大值为 100", required = true, example = "10")@NotNull(message = "每页条数不能为空")@Min(value = 1, message = "每页条数最小值为 1")@Max(value = 100, message = "每页条数最大值为 100")private Integer pageSize = PAGE_SIZE;
}
- 解析:
- pageNo:页码,默认 1,校验非空且 ≥ 1。
- pageSize:每页条数,默认 10,校验非空,范围 1-100。
- 使用 @Schema 提供 OpenAPI 文档描述。
搜索条件 TenantPageReqVO
TenantPageReqVO 继承 PageParam,添加搜索条件。
@Schema(description = "管理后台 - 租户分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TenantPageReqVO extends PageParam {@Schema(description = "租户名", example = "芋道")private String name;@Schema(description = "联系人", example = "芋艿")private String contactName;@Schema(description = "联系手机", example = "15601691300")private String contactMobile;@Schema(description = "租户状态(0正常 1停用)", example = "1")private Integer status;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@Schema(description = "创建时间")private LocalDateTime[] createTime;
}
- 解析:
- 继承 pageNo 和 pageSize。
- 搜索字段:name、contactName、contactMobile 支持模糊查询,status 支持精确匹配,createTime 支持时间范围查询。
- @DateTimeFormat 解析时间字符串(如 2025-05-14 09:47:00)。
分页结果 PageResult
分页结果使用 PageResult 类封装。
@Schema(description = "分页结果")
@Data
public final class PageResult<T> implements Serializable {@Schema(description = "数据", required = true)private List<T> list; // 数据列表@Schema(description = "总量", required = true)private Long total; // 总记录数
}
解析:
- list:当前页的数据列表,泛型支持任意类型(如 TenantRespVO)。
- total:总记录数,用于前端分页组件计算总页数。
2.2 Mapper 查询
后端使用 MyBatis Plus 的分页功能,TenantMapper 定义分页查询。
@Mapper
public interface TenantMapper extends BaseMapperX<TenantDO> {default PageResult<TenantDO> selectPage(TenantPageReqVO reqVO) {return selectPage(reqVO, new LambdaQueryWrapperX<TenantDO>().likeIfPresent(TenantDO::getName, reqVO.getName()) // 模糊查询租户名.likeIfPresent(TenantDO::getContactName, reqVO.getContactName()) // 模糊查询联系人.likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile()) // 模糊查询联系手机.eqIfPresent(TenantDO::getStatus, reqVO.getStatus()) // 精确匹配状态.betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime()) // 时间范围查询.orderByDesc(TenantDO::getId)); // 按 ID 倒序}
}
- 功能:执行分页查询,根据 TenantPageReqVO 的条件构建动态 SQL。
- 逻辑:
- 使用 LambdaQueryWrapperX(MyBatis Plus 增强封装)动态拼接查询条件。
- likeIfPresent:当字段非空时添加模糊查询(如 name LIKE '%芋道%')。
- eqIfPresent:当字段非空时添加精确匹配。
- betweenIfPresent:当 createTime 非空时添加时间范围查询。
- orderByDesc:按 id 倒序排序。
- 返回:PageResult<TenantDO>,包含当前页数据和总记录数。
MyBatis Plus 分页封装
BaseMapperX 封装了 MyBatis Plus 的分页逻辑。
default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {return selectPage(pageParam, null, queryWrapper);
}default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param("ew") Wrapper<T> queryWrapper) {// 特殊:不分页,直接查询全部if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {List<T> list = selectList(queryWrapper);return new PageResult<>(list, (long) list.size());}// MyBatis Plus 查询IPage<T> mpPage = MyBatisUtils.buildPage(pageParam, sortingFields);selectPage(mpPage, queryWrapper);// 转换返回return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
}
解析:
- PageParam:提供 pageNo 和 pageSize。
- IPage:MyBatis Plus 的分页对象,设置页码和条数。
- MyBatisUtils.buildPage:将 PageParam 转换为 IPage。
- 特殊情况:当 pageSize 为 PAGE_SIZE_NONE 时,查询所有数据。
- 转换:将 IPage 的 records 和 total 转为 PageResult。