目录
今天在使用这个Pagination分页的时候,有一个小坑,坑了我不少时间,记录一下
写在前面
前端使用vue3+elementplus,后端使用springboot+mybatisplus
对于elementplus的安装,这里就不多说了,直接开始使用。
简单使用
mybatis-plus分页
我们查看mybatis-plus的官方文档,直接使用他的分页插件
假设我需要分页查询sku(一个商城系统的术语,你可以粗略的理解为商品信息)的数据,假设我们已经有了一个实体类和service接口以及实现类,并且这个接口继承了mybatis-plus的IService接口,实现类继承mybatis-plus的ServiceImpl类,否则我们就不能使用它的CRUD接口。
//实体类
@Data
@TableName("my_sku_info")
public class SkuInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("spu_id")
private Long spuId;
@TableField("sku_name")
private String skuName;
/**
* 描述
*/
@TableField("sku_desc")
private String skuDesc;
@TableField("category_id")
private Long categoryId;
@TableField("brand_id")
private Long brandId;
@TableField("sku_default_img")
private Long skuDefaultImg;
/**
* 标题
*/
@TableField("sku_title")
private String skuTitle;
@TableField("price")
private BigDecimal price;
/**
* 副标题
*/
@TableField("sku_subtitle")
private String skuSubtitle;
/**
*售卖量
*/
@TableField("sale_count")
private String saleCount;
@TableField(exist = false)
private FileInfoVo fileInfoVo;
}
//service接口
public interface ISkuInfoService extends IService<SkuInfo> {
}
//service实现类
@Service
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoMapper, SkuInfo> implements ISkuInfoService {
}
我们需要调用它的page方法,通过文档我们知道需要传入一个 IPage类型的对象,假设我们有一个GoodsServiceImpl 类来处理所有的逻辑
@Service
@Slf4j
public class GoodsServiceImpl implements IGoodsService {
@Autowired
private ISkuInfoService skuInfoService;
@Override
public Page getSkuList(Integer pageNum, Integer pageSize) {
Page<SkuInfo> page = skuInfoService.page(new Page<SkuInfo>(pageNum, pageSize));
return page;
}
}
controller接受前端传入的分页数据
@RestController
@RequestMapping("/goods/")
@Slf4j
public class GoodsController {
@Autowired
IGoodsService goodsService;
@GetMapping("get/sku/list/{pageNum}/{pageSize}")
public R getSkuList(@PathVariable Integer pageNum, @PathVariable Integer pageSize, ) {
Page list = goodsService.getSkuList(pageNum, pageSize);
return RUtils.successData(list);
}
}
到这,我们后端已经简单完成了。
elementplus的分页器
我们先来写一个axios请求
export function getSkuList(pageNum, pageSize) {
return request({
method: 'get',
url: goodsBase + `get/sku/list/${pageNum}/${pageSize}`,
})
}
在这里我们需要再次查看mybatis-plus的文档,我们需要知道这个返回的page有哪些属性,因为在前端中我们需要获取这些数据并处理
<template>
<-- 其他代码,此处忽略 -->
<el-pagination
hide-on-single-page
v-model:current-page="pageInfo.pageNum"
v-model:page-size="pageInfo.pageSize"
:total="pageInfo.totals"
/>
</template>
<script setup>
import {onMounted, reactive, ref} from "vue";
import {getSkuList, modifySku, getSpuAtlas} from '@/api/home/goods/goods'
/*数据封装区域*/
let pageInfo = reactive({
pageNum: 1,
pageSize: 5,
totals: Number
})
/*初始化区域*/
onMounted(() => {
getSkuListData(pageInfo.pageNum, pageInfo.pageSize)
})
/*方法事件区域*/
async function getSkuListData(pageNum, pageSize) {
let res = await getSkuList(pageNum, pageSize)
/*封装分页信息*/
pageInfo.totals = res.data.data.total - 0
pageInfo.pageNum = res.data.data.current - 0
skuList.value = res.data.data.records
}
//监听
watch(()=>pageInfo.pageNum, (newValue) => {
getSkuListData(pageInfo.pageNum, pageInfo.pageSize)
})
</script>
在官网中说不推荐使用事件
所以在上面的代码中,我使用事件监听来监听:current-page 和 page-size 的改变,他们的值只要改变了,就要发送请求获取对应的页码的table数据
踩坑
注意后端传入的分页数据很可能是字符串类型的,我们不能直接对分页器赋值,需要转换为数字类型,也就是-0就行了,否则可能会出现分页器消失的情况
大工告成!
配合搜索条件使用
在实际情况中,我们一般不会简单分页查询就完事了,时常会伴有条件查询。
在上面的代码上,我需要添加一些条件。由于搜索条件一般只会在这一个地方使用,我就不用dto实体类封装了,直接使用map封装
后端:
@Service
@Slf4j
public class GoodsServiceImpl implements IGoodsService {
@Autowired
private ISkuInfoService skuInfoService;
@Override
public Page getSkuList(Integer pageNum, Integer pageSize, Map<String, Object> skuSearchDto) {
/*管管理员可以查看所有Sku信息
* 非管理员只能查看自己的sku信息
* 由于sku表并没有商家的字段,而spu表有,需要查出商家的所有spu,然后查出所有spu下的sku*/
long adminId = StpUtil.getLoginIdAsLong();//使用satoken框架获取登录人的id
List<SpuInfo> spuInfoList = null;
boolean isAdmin = adminUserService.isAdmin(adminId);
QueryWrapper<SkuInfo> wrapper = new QueryWrapper<>();
if (!isAdmin) {
QueryWrapper<SpuInfo> spuWraper = new QueryWrapper<>();
spuWraper.eq("business_id", adminId);
spuInfoList = spuInfoService.list(spuWraper);
if (spuInfoList == null || spuInfoList.size() == 0) {
return null;//如果不是管理员,并且没有SPU,不需要查询sku了,直接返回,
} else {
List<Long> spuIds = spuInfoList.stream().map(SpuInfo::getId).collect(Collectors.toList());
wrapper.in("spu_id", spuIds);
}
}
String brandId = (String) skuSearchDto.get("brandId");
if (!StringUtils.isEmptyString(brandId)) {
wrapper.eq("brand_id", skuSearchDto.get("brandId"));
}
String categoryId = (String) skuSearchDto.get("categoryId");
if (!StringUtils.isEmptyString(categoryId)) {
wrapper.eq("category_id", skuSearchDto.get("categoryId"));
}
String state = (String) skuSearchDto.get("state");
if (!StringUtils.isEmptyString(state)) {
wrapper.eq("state", skuSearchDto.get("state"));
}
String serachValue = (String) skuSearchDto.get("serachValue");
if (!StringUtils.isEmptyString(serachValue)) {
wrapper.like("sku_name", skuSearchDto.get("serachValue"))
.or().like("sku_subtitle", skuSearchDto.get("serachValue"))
.or().like("sku_title",serachValue);
}
Integer minPrice = Integer.valueOf(skuSearchDto.get("minPrice").toString());
if (minPrice != 0) {
wrapper.ge("price", minPrice);
}
Integer maxPrice = Integer.valueOf(skuSearchDto.get("maxPrice").toString());
if (maxPrice != 0) {
wrapper.le("price", maxPrice);
}
Page<SkuInfo> page = skuInfoService.page(new Page<SkuInfo>(pageNum, pageSize), wrapper);
page.getRecords().forEach(item -> {
if (item.getSkuDefaultImg() != null) {
FileInfoVo infoVoFromFIles = fileService.getInfoVoFromFIles(item.getSkuDefaultImg());
item.setFileInfoVo(infoVoFromFIles);
}
});
return page;
}
}
controller类
@RestController
@RequestMapping("/goods/")
@Slf4j
public class GoodsController {
@Autowired
IGoodsService goodsService;
@GetMapping("get/sku/list/{pageNum}/{pageSize}")
public R getSkuList(@PathVariable Integer pageNum, @PathVariable Integer pageSize, @RequestParam Map<String, Object> skuSearchDto) {
Page list = goodsService.getSkuList(pageNum, pageSize, skuSearchDto);
return RUtils.successData(list);
}
}
前端,我们需要修改一下axios请求,添加一个封装条件的变量
export function getSkuList(pageNum, pageSize, skuSearchDto) {
return request({
method: 'get',
url: goodsBase + `get/sku/list/${pageNum}/${pageSize}`,
params: skuSearchDto
})
}
<template>
<div>
<!-- 检索条件-->
<el-form :inline="true" :model="skuSearchDto" ref="searchRef">
<el-form-item label="分类" prop="categoryId">
<el-cascader v-model="skuSearchDto.categoryId" :options="allCategoryList" @visible-change="getCategoryList"
:props="cascaderProps"/>
</el-form-item>
<el-form-item label="品牌" prop="brandId">
<el-select v-model="skuSearchDto.brandId" class="m-2" placeholder="Select" size="large"
@visible-change="getBrandList" :disabled="skuSearchDto.categoryId.length==0">
<el-option
v-for="item in brandList"
:key="item.id"
:label="item.brandName"
:value="item.brandId"
/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="skuSearchDto.state" class="m-2" placeholder="Select" size="large">
<el-option
label="上架"
value="0"
/>
<el-option
label="下架"
value="1"
/>
</el-select>
</el-form-item>
<el-form-item label="价格" prop="Price">
<el-input-number v-model="skuSearchDto.minPrice" :min="1" @change="handleChange"/>
-
<el-input-number v-model="skuSearchDto.maxPrice" :min="skuSearchDto.minPrice" @change="handleChange"/>
</el-form-item>
<el-form-item label="检索" prop="serachValue">
<el-input v-model="skuSearchDto.serachValue"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="skuSearch">搜索</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchRef.resetFields()">重置</el-button>
</el-form-item>
</el-form>
</div>
<-- 其他代码-->
<el-pagination
hide-on-single-page
v-model:current-page="pageInfo.pageNum"
v-model:page-size="pageInfo.pageSize"
:page-sizes="[3, 5, 8]"
:small="small"
:background="background"
layout="total, sizes, prev, pager, next, jumper"
:total="pageInfo.totals"
/>
</template>
<script setup>
import {getSkuList, modifySku, getSpuAtlas} from '@/api/home/goods/goods'
let skuSearchDto = reactive({
categoryId: '',
brandId: '',
state: '',
serachValue: '',
minPrice: 0,
maxPrice: 0
})
let pageInfo = reactive({
pageNum: 1,
pageSize: 5,
totals: Number
})
async function getSkuListData(pageNum, pageSize, skuSearchDto) {
let res = await getSkuList(pageNum, pageSize, skuSearchDto)
/*封装分页信息*/
try {
pageInfo.totals = res.data.data.total - 0
pageInfo.pageNum = res.data.data.current - 0
skuList.value = res.data.data.records
for (let i = 0; i < skuList.value.length; i++) {
if (skuList.value[i].skuDefaultImg != null) {
skuList.value[i].imgBase64Str = await getImageToBase64FromFileId(skuList.value[i].skuDefaultImg)
}
}
} catch (e) {
console.log("这里捕获了一个异常,多半是后端查出的数据是空的,返回了一个null,或者是默认图片转换失败", e)
}
}
function skuSearch() {
getSkuListData(pageInfo.pageNum, pageInfo.pageSize, skuSearchDto)
}
onMounted(() => {
skuSearch()
})
watch([()=>pageInfo.pageNum,()=>pageInfo.pageSize], (newValue) => {
//这里使用了skuSearchDto,不能使用watchEffect,否则在skuSearchDto变化的时候也会触发
skuSearch()
})
完成!