RuoYi-Vue3表格组件封装:Pagination分页与数据筛选实现
引言:企业级后台表格交互的痛点与解决方案
在中后台系统开发中,表格组件是数据展示的核心载体,而分页控制与数据筛选则是用户高频交互的功能模块。RuoYi-Vue3作为基于Vue3 & Vite、Element Plus的前后端分离权限管理系统,其表格组件的设计直接影响开发效率与用户体验。本文将深入剖析RuoYi-Vue3中Pagination分页组件的封装原理,详解数据筛选功能的实现方案,并通过实战案例展示如何构建高效、可复用的表格交互系统。
读完本文你将掌握:
- 基于Element Plus二次封装通用分页组件的完整流程
- 多条件组合筛选与高级搜索的实现策略
- 表格数据状态管理与性能优化技巧
- 企业级表格交互的最佳实践与设计模式
Pagination分页组件的深度封装
组件设计理念与功能拆解
RuoYi-Vue3的Pagination组件基于Element Plus的el-pagination进行二次封装,通过props透传与事件封装,实现了一套适应复杂业务场景的通用分页解决方案。其核心设计目标包括:
- 参数标准化:统一分页参数命名与数据格式
- 状态同步:实现页码与页大小的双向绑定
- 行为一致:封装通用分页逻辑,确保跨页面交互统一
- 扩展性:预留自定义布局与样式接口
<template>
<div :class="{ 'hidden': hidden }" class="pagination-container">
<el-pagination
:background="background"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
核心参数与事件设计
组件通过props接收7个核心参数,覆盖分页控制的全部要素:
| 参数名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| total | Number | - | 总记录数(必填) |
| page | Number | 1 | 当前页码 |
| limit | Number | 20 | 每页记录数 |
| pageSizes | Array | [10,20,30,50] | 每页条数选择器选项 |
| pagerCount | Number | 5/7 | 页码按钮数量(移动端5个,桌面端7个) |
| layout | String | "total, sizes, prev, pager, next, jumper" | 分页布局 |
| background | Boolean | true | 是否显示背景色 |
事件系统设计了两种交互反馈机制:
- size-change:页大小变更事件
- current-change:页码变更事件
- 统一回调:通过pagination事件返回合并后的分页参数
<script setup>
import { scrollTo } from '@/utils/scroll-to'
const props = defineProps({
total: { required: true, type: Number },
page: { type: Number, default: 1 },
limit: { type: Number, default: 20 },
// 其他参数...
})
const emit = defineEmits()
const currentPage = computed({
get() { return props.page },
set(val) { emit('update:page', val) }
})
const pageSize = computed({
get() { return props.limit },
set(val) { emit('update:limit', val) }
})
function handleSizeChange(val) {
if (currentPage.value * val > props.total) {
currentPage.value = 1 // 页码超限自动重置
}
emit('pagination', { page: currentPage.value, limit: val })
if (props.autoScroll) scrollTo(0, 800) // 滚动到顶部
}
</script>
响应式适配与用户体验优化
组件内置了响应式处理逻辑,通过监听窗口宽度自动调整页码显示数量:
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7
}
同时实现了两大体验优化:
- 页码重置机制:当用户切换页大小时,如果当前页码超出新的总页数,自动重置为第1页
- 滚动定位:分页变更后自动滚动到页面顶部,避免数据刷新后用户视线丢失
数据筛选系统的实现策略
多维度筛选表单设计
RuoYi-Vue3采用el-form与el-table联动的方式实现数据筛选,典型的用户管理页面筛选表单包含:
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" type="daterange" range-separator="-"
start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
筛选参数处理与API集成
在提交筛选条件前,需要进行参数格式化与整合,核心逻辑位于src/utils/ruoyi.js:
// 添加日期范围
export function addDateRange(params, dateRange, propName) {
let search = params
search.params = typeof(search.params) === 'object' && search.params !== null
&& !Array.isArray(search.params) ? search.params : {}
dateRange = Array.isArray(dateRange) ? dateRange : []
if (typeof(propName) === 'undefined') {
search.params['beginTime'] = dateRange[0]
search.params['endTime'] = dateRange[1]
} else {
search.params['begin' + propName] = dateRange[0]
search.params['end' + propName] = dateRange[1]
}
return search
}
// 参数处理
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
var part = encodeURIComponent(propName) + "="
if (value !== null && value !== "" && typeof(value) !== "undefined") {
// 对象参数序列化
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof(value[key]) !== 'undefined') {
let params = propName + '[' + key + ']'
var subPart = encodeURIComponent(params) + "="
result += subPart + encodeURIComponent(value[key]) + "&"
}
}
} else {
result += part + encodeURIComponent(value) + "&"
}
}
}
return result
}
在API请求中的使用示例:
// 查询用户列表
function getList() {
loading.value = true
listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
loading.value = false
userList.value = res.rows
total.value = res.total
})
}
高级筛选功能实现
1. 字典数据联动
系统通过useDict钩子函数实现数据字典与筛选表单的联动:
const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex")
在模板中直接使用:
<el-select v-model="queryParams.status" placeholder="角色状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
2. 树形结构筛选
部门筛选采用树形选择器实现层级筛选:
<el-tree-select v-model="form.deptId" :data="enabledDeptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id" placeholder="请选择归属部门" clearable check-strictly />
3. 排序功能
通过表格列的排序事件实现数据排序:
<el-table @sort-change="handleSortChange">
<el-table-column label="访问时间" prop="loginTime" sortable="custom"
:sort-orders="['descending', 'ascending']" />
</el-table>
<script setup>
function handleSortChange(column) {
queryParams.value.orderByColumn = column.prop
queryParams.value.isAsc = column.order === 'ascending' ? 'asc' : 'desc'
getList()
}
</script>
分页与筛选的集成应用
完整业务流程实现
以下是用户管理页面的完整实现流程,展示分页与筛选如何协同工作:
<template>
<div class="app-container">
<!-- 筛选表单 -->
<el-form :model="queryParams" ref="queryRef" :inline="true">
<!-- 表单内容 -->
</el-form>
<!-- 操作按钮区 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:user:add']">新增</el-button>
</el-col>
<!-- 其他按钮 -->
</el-row>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<!-- 表格列定义 -->
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total"
v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</div>
</template>
<script setup>
import { listUser } from "@/api/system/user"
const userList = ref([])
const loading = ref(true)
const total = ref(0)
const dateRange = ref([])
const queryParams = ref({
pageNum: 1,
pageSize: 10,
userName: undefined,
phonenumber: undefined,
status: undefined,
deptId: undefined
})
// 查询用户列表
function getList() {
loading.value = true
listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
loading.value = false
userList.value = res.rows
total.value = res.total
})
}
// 搜索按钮操作
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
// 重置按钮操作
function resetQuery() {
dateRange.value = []
proxy.resetForm("queryRef")
queryParams.value.pageNum = 1
getList()
}
// 分页回调
function handleSizeChange(val) {
queryParams.value.pageSize = val
getList()
}
// 初始化加载数据
getList()
</script>
组件通信与状态管理
分页与筛选组件之间通过以下方式实现通信:
- 参数传递:父组件通过props向Pagination组件传递total、page、limit等参数
- 事件回调:Pagination组件通过pagination事件向父组件传递分页变更
- 状态同步:使用v-model实现页码和页大小的双向绑定
<pagination
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
性能优化策略
- 请求防抖:虽然RuoYi-Vue3未直接实现,但可通过以下方式添加:
import { debounce } from 'lodash'
const handleQuery = debounce(() => { getList() }, 500)
- 缓存机制:对字典数据等静态资源进行缓存:
// 在dict.js中实现缓存逻辑
const dictCache = new Map()
export function getDictCached(type) {
if (dictCache.has(type)) {
return Promise.resolve(dictCache.get(type))
}
return getDicts(type).then(res => {
dictCache.set(type, res.data)
return res.data
})
}
- 虚拟滚动:对于大数据量表格,可集成虚拟滚动:
<el-table v-loading="loading" :data="userList" height="600" v-infinite-scroll="loadMore">
<!-- 表格内容 -->
</el-table>
最佳实践与扩展技巧
分页组件的定制化
通过layout属性自定义分页布局:
<!-- 精简版分页 -->
<pagination layout="prev, pager, next" />
<!-- 完整版分页 -->
<pagination layout="total, sizes, prev, pager, next, jumper" />
筛选条件的持久化
使用localStorage保存用户的筛选偏好:
// 保存筛选条件
function saveQueryParams() {
localStorage.setItem('userQueryParams', JSON.stringify(queryParams.value))
}
// 恢复筛选条件
function restoreQueryParams() {
const saved = localStorage.getItem('userQueryParams')
if (saved) queryParams.value = JSON.parse(saved)
}
onMounted(() => {
restoreQueryParams()
getList()
})
复杂查询构建器
对于更复杂的查询需求,可实现查询构建器功能:
<el-collapse>
<el-collapse-item title="高级搜索">
<!-- 高级搜索条件 -->
</el-collapse-item>
</el-collapse>
总结与展望
RuoYi-Vue3的表格组件封装充分利用了Vue3的组合式API和Element Plus的组件化特性,通过Pagination组件的二次封装和筛选系统的模块化设计,实现了高效、灵活的数据交互方案。核心优势包括:
- 组件复用:Pagination组件在15+页面中复用,减少重复代码
- 交互统一:标准化的分页与筛选交互,降低用户学习成本
- 性能优化:内置的滚动定位和页码重置机制提升用户体验
- 扩展性强:支持自定义布局、排序、高级筛选等复杂需求
未来可以进一步优化的方向:
- 实现筛选条件的可视化配置
- 集成更高级的表格功能(如行内编辑、树形表格)
- 增强大数据量处理能力(虚拟滚动、前端分页)
通过本文的技术方案,开发者可以快速构建企业级的表格交互系统,同时保持代码的可维护性和扩展性。建议在实际项目中根据业务复杂度灵活调整分页与筛选策略,平衡用户体验与系统性能。
点赞+收藏+关注,获取更多RuoYi-Vue3深度技术解析,下期预告:《动态表单设计与代码生成》。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



