RuoYi-Vue3表格组件封装:Pagination分页与数据筛选实现

RuoYi-Vue3表格组件封装:Pagination分页与数据筛选实现

【免费下载链接】RuoYi-Vue3 :tada: (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统 【免费下载链接】RuoYi-Vue3 项目地址: https://gitcode.com/GitHub_Trending/ruo/RuoYi-Vue3

引言:企业级后台表格交互的痛点与解决方案

在中后台系统开发中,表格组件是数据展示的核心载体,而分页控制与数据筛选则是用户高频交互的功能模块。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个核心参数,覆盖分页控制的全部要素:

参数名类型默认值描述
totalNumber-总记录数(必填)
pageNumber1当前页码
limitNumber20每页记录数
pageSizesArray[10,20,30,50]每页条数选择器选项
pagerCountNumber5/7页码按钮数量(移动端5个,桌面端7个)
layoutString"total, sizes, prev, pager, next, jumper"分页布局
backgroundBooleantrue是否显示背景色

事件系统设计了两种交互反馈机制:

  • 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. 页码重置机制:当用户切换页大小时,如果当前页码超出新的总页数,自动重置为第1页
  2. 滚动定位:分页变更后自动滚动到页面顶部,避免数据刷新后用户视线丢失

数据筛选系统的实现策略

多维度筛选表单设计

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>

组件通信与状态管理

分页与筛选组件之间通过以下方式实现通信:

  1. 参数传递:父组件通过props向Pagination组件传递total、page、limit等参数
  2. 事件回调:Pagination组件通过pagination事件向父组件传递分页变更
  3. 状态同步:使用v-model实现页码和页大小的双向绑定
<pagination 
  v-model:page="queryParams.pageNum" 
  v-model:limit="queryParams.pageSize" 
  @pagination="getList" 
/>

性能优化策略

  1. 请求防抖:虽然RuoYi-Vue3未直接实现,但可通过以下方式添加:
import { debounce } from 'lodash'
const handleQuery = debounce(() => { getList() }, 500)
  1. 缓存机制:对字典数据等静态资源进行缓存:
// 在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
  })
}
  1. 虚拟滚动:对于大数据量表格,可集成虚拟滚动:
<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组件的二次封装和筛选系统的模块化设计,实现了高效、灵活的数据交互方案。核心优势包括:

  1. 组件复用:Pagination组件在15+页面中复用,减少重复代码
  2. 交互统一:标准化的分页与筛选交互,降低用户学习成本
  3. 性能优化:内置的滚动定位和页码重置机制提升用户体验
  4. 扩展性强:支持自定义布局、排序、高级筛选等复杂需求

未来可以进一步优化的方向:

  • 实现筛选条件的可视化配置
  • 集成更高级的表格功能(如行内编辑、树形表格)
  • 增强大数据量处理能力(虚拟滚动、前端分页)

通过本文的技术方案,开发者可以快速构建企业级的表格交互系统,同时保持代码的可维护性和扩展性。建议在实际项目中根据业务复杂度灵活调整分页与筛选策略,平衡用户体验与系统性能。

点赞+收藏+关注,获取更多RuoYi-Vue3深度技术解析,下期预告:《动态表单设计与代码生成》。

【免费下载链接】RuoYi-Vue3 :tada: (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统 【免费下载链接】RuoYi-Vue3 项目地址: https://gitcode.com/GitHub_Trending/ruo/RuoYi-Vue3

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值