ElementPlus中的分页逻辑与实现

ElementPlus中的分页逻辑与实现

分页是web开发中必不可少的组件,element团队提供了简洁美观的分页组件,配合table数据可以实现即插即用的分页效果。分页的实现可以分成两种,一是前端分页,二是后端分页。这两种分页分别适用于不同的业务场景,分页写了无数回了,但一直记不住,因此记录一下这两种分页效果的逻辑和实现。

一、前端分页

前端分页适用于数据量少的情况,向后端发起一次数据请求,前端处理分页。优点就是接口请求少,逻辑很简单,缺点很明显,处理大量数据时,效率极低。

前端分页是我非常喜欢的一种分页模式,最主要的原因就是代码简单。

现在来看看是怎么实现的。

首先看看最终的实现效果:

image-20231214083706881

配合代码来看:

<template>
    <div class="outer">
        <el-table :data="currentTableData" height="480" stripe border class="table">
            <el-table-column v-for="(item, index) in tableForm" :key="index" :prop="item.prop" :label="item.label"
                :width="100" show-overflow-tooltip></el-table-column>
            <el-table-column fixed="right" label="详细" width="100">
                <template #default="scope">
                    <el-button link type="primary" size="small" @click="handleClick(scope.$index, scope.row)">查看</el-button>
                </template>
            </el-table-column>
        </el-table>
        <el-pagination class="pagination" small background layout="prev, pager, next" :total="totalItems"
            v-model:current-page="currentPage" @current-change="handelCurrentChange" :hide-on-single-page="paginationShow"
            style="margin-top: 20px;" />
    </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue'
import { getAnalisisNolocalTableApi } from '@/apis/analysisNolocal'
import { ElMessage } from 'element-plus';
const tableData = ref([])
const tableForm = [
   // 表头数据
]

// 点击每行的查看,展示详细事故信息
import mitt from '@/utils/mitt'
const emitter = mitt
const handleClick = (index, row) => {
    emitter.emit('showDrawer', row)
    // console.log(index, row)
}

// 分页
const currentTableData = ref([])
const currentPage = ref(1)
const pageSize = 10
const totalItems = ref(0)
const paginationShow = ref(true)

watch(tableData, (newVal, oldVal) => {
    currentPage.value = 1
    totalItems.value = tableData.value.length
    currentTableData.value = tableData.value.filter((item, index) => index < pageSize)
    // paginationShow.value = tableData.value.length > 10 ? true : false
})

const handelCurrentChange = page => {
    currentPage.value = page
    // currentPage.value = 1
    const index = pageSize * (page - 1)
    const nums = pageSize * page
    const tables = []
    for (let i = index; i < nums; i++) {
        if (tableData.value[i]) tables.push(tableData.value[i])
    }
    currentTableData.value = tables
}

const getData = async () => {
    try {
        const { data } = await getAnalisisNolocalTableApi()
        // console.log(data)
        tableData.value = data
    } catch (error) {
        ElMessage.error('请求接口报错')
        console.log(error)
    }
}
onMounted(async () => {
    getData()
})
</script>

<style lang="scss" scoped>

</style>

首先解释一下代码:

  1. 表格中的全部数据绑定的是tableData,获取tableData的方法是getData,在组件挂载前即调用
  2. 当前页面的表格数据是currentTableData
  3. 表格的表头是tableForm,这里根据自己的实际情况去写

接着看分页:

<el-pagination class="pagination" small background layout="prev, pager, next" :total="totalItems"
   v-model:current-page="currentPage" @current-change="handelCurrentChange" :hide-on-single-page="paginationShow"
   style="margin-top: 20px;" />

参数非常多,我们直接看elementplus给的api:

属性名说明类型默认值
small是否使用小型分页样式booleanfalse
background是否为分页按钮添加背景色booleanfalse
page-size / v-model:page-size每页显示条目个数number
default-page-size每页默认的条目个数,不设置时默认为10number
total总条目数number
page-count总页数, totalpage-count 设置任意一个就可以达到显示页码的功能;如果要支持 page-sizes 的更改,则需要使用 total 属性number
pager-count设置最大页码按钮数。 页码按钮的数量,当总页数超过该值时会折叠number7
current-page / v-model:current-page当前页数number
default-current-page当前页数的默认初始值,不设置时默认为 1number
layout组件布局,子组件名用逗号分隔stringprev, pager, next, jumper, ->, total
page-sizes每页显示个数选择器的选项设置object[10, 20, 30, 40, 50, 100]
popper-class每页显示个数选择器的下拉框类名string‘’
prev-text替代图标显示的上一页文字string‘’
prev-icon上一页的图标, 比 prev-text 优先级更高string / ComponentArrowLeft
next-text替代图标显示的下一页文字string‘’
next-icon下一页的图标, 比 next-text 优先级更低string / ComponentArrowRight
disabled是否禁用分页booleanfalse
teleported 2.3.13是否将下拉菜单teleport至 bodybooleantrue
hide-on-single-page只有一页时是否隐藏booleanfalse

有这么几个参数很重要:

  • const currentPage = ref(1),绑定的属性是current-page / v-model:current-page,即当前页码,默认为1
  • const totalItems = ref(0),绑定的属性是total,就是数据总数,根据tableData的长度来确定

还有一个事件很重要,即current-change,就是当前页码发生变化的时候执行的事件,绑定的方法是handelCurrentChange,来看看这个方法做了什么事情

const handelCurrentChange = page => {
    currentPage.value = page
    // currentPage.value = 1
    const index = pageSize * (page - 1)
    const nums = pageSize * page
    const tables = []
    for (let i = index; i < nums; i++) {
        if (tableData.value[i]) tables.push(tableData.value[i])
    }
    currentTableData.value = tables
}
  1. 首先这个方法接收一个默认的参数page,其实就是当前的页码,把默认参数赋值给currentPage
  2. 获取当前页数据的起始索引,即index = pageSize * (page - 1),因为页码是从1开始,第一页的数据从第0条开始,所以其实索引应该是pageSize * (page - 1),这里的pageSize 就是每页数据要显示的条数
  3. 获取当前页最后一条数据的索引,即nums = pageSize * page
  4. 根据index和nums来获取当前页的数据

注意看,我还写了一个监听事件:

watch(tableData, (newVal, oldVal) => {
    currentPage.value = 1
    totalItems.value = tableData.value.length
    currentTableData.value = tableData.value.filter((item, index) => index < pageSize)
    // paginationShow.value = tableData.value.length > 10 ? true : false
})

这个代码写在这里是因为我还对数据做了筛选,筛选数据后,tableData发生变化,所以分页的总数和当前页都需要变化,这和我这里讲的前端分页关系不大

至此,就实现了前端分页的全部效果,还是挺简单的,总结一下,核心在于:

  1. 定义分页的参数
  2. 向后端获取总的数据
  3. 写好current-change方法

二、后端分页

其实后端分页才是正确的思路,因为正常的情况下,后端不会一下子把全部的数据都给前端,传输效率低而且也不安全。但后端分页相对来说要麻烦很多,不管咋样,还是记录一下

先看看我的分页结果:

在这里插入图片描述

都是用的测试数据,分页在右下角,其实在显示上没有任何差别,但逻辑完全不一样

1.后端代码

后端写了俩接口,我用node写的,一是获取列表总数,二是监听换页返回给前端当前页面的数据

代码如下:

// 分页
// 获取列表总数
exports.getAdminListLength = (req, res) => {
  const sql = "select * from users where identity = ?";
  db.query(sql, req.body.identity, (err, results) => {
    if (err) res.cc(err);
    res.send({
      length: results.length,
    });
  });
};

// 监听换页,返回数据,参数为页码和身份
exports.returnListData = (req, res) => {
  // 每页显示10条,offset是起始处的偏移量
  const number = req.body.page * 10;
  const sql = `select * from users where identity = ? limit 10 offset ${number}`;
  db.query(sql, [req.body.identity, number], (err, results) => {
    if (err) res.cc(err);
    results.forEach((item) => {
      item.password = "";
      item.create_time = "";
      item.update_time = item.update_time.slice(0, 19);
    });
    res.send(results);
  });
};

获取列表总数没啥好说的,就是一个查询语句

主要看returnListData方法

其实前端给后端传递了两个参数,一个是当前的页码(page),一个是查询条件(identity)

看查询语句

const sql = `select * from users where identity = ? limit 10 offset ${number}`;

limit 10表示返回前10条数据

这里的offset很关键,表示从哪里开始返回10条数据,比如我想要查询第3页的数据,那么前端实际的page=3,传递给后端实际的page=page-1=2(这里的逻辑要理解一下),那么,第3页的数据应该是索引20-29的数据,这里的number=20,offset 20的意思就是从第20条开始取数据

后端的逻辑就是这样

2.前端代码

直接上代码

<template>
    <BreadCrumb ref="breadCrumb" :item="item"></BreadCrumb>
    <div class="table-wrapped">
        <div class="table-top">
            <div class="table-header">
                <div class="search-wrapped" style="display: flex">
                    <el-input v-model="input1" class="w-50 m-2" placeholder="输入账号搜索" :prefix-icon="Search"
                        @change="searchAdmin" />
                    <!-- <el-button type="primary" @click="getAdmin" style="margin-left: 10px;" circle :icon="Refresh"
                        title="重置列表"></el-button> -->
                </div>
                <div class="button-wrapped">
                    <el-button type="primary" @click="create">添加产品管理员</el-button>
                </div>
            </div>
            <div class="table-content">
                <el-table :data="tableData" border style="width: 100%">
                    <el-table-column type="index" width="50" />
                    <el-table-column prop="account" label="账号" />
                    <el-table-column prop="name" label="姓名" />
                    <el-table-column prop="sex" label="性别" />
                    <el-table-column prop="department" label="部门" />
                    <el-table-column prop="email" label="邮箱" />
                    <el-table-column prop="update_time" label="更新时间" />

                    <el-table-column label="操作" width="150">
                        <template #default="scope">
                            <el-button type="success" size="small"
                                @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
                            <el-button type="danger" size="small"
                                @click="handleDelete(scope.$index, scope.row)">删除</el-button>
                        </template>
                    </el-table-column>

                </el-table>
            </div>
        </div>
        <div class="table-footer">
            <el-pagination :page-size="2" :current-page="paginationData.currentPage" :pager-count="5" :total="adminTotal"
                :page-count="paginationData.pageCount" @current-change="currentPageChange" layout="prev, pager, next" />
        </div>
    </div>
    <CreateAdmin></CreateAdmin>
    <EditAdmin></EditAdmin>
    <DeleteAdmin></DeleteAdmin>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { Refresh, Search } from '@element-plus/icons-vue'
import BreadCrumb from '@/components/BreadCrumb.vue';
import CreateAdmin from '../components/CreateAdmin.vue'
import EditAdmin from '../components/EditAdmin.vue'
import DeleteAdmin from '../components/DeleteAdmin.vue';
import { getAdminAPI, searchUserAPI, getAdminListLengthAPI, returnListDataAPI } from "@/apis/userinfo";
import mitt from '@/utils/mitt'
import { ElMessage } from 'element-plus';
const emitter = mitt

const item = ref({
    first: '用户管理',
    second: '产品管理员'
})
const input1 = ref('')
const tableData = ref([])

const create = () => {
    emitter.emit('openCreateDialog', '添加产品管理员')
}

const handleEdit = (index, row) => {
    emitter.emit('openEditDialog', { index, row, title: '编辑产品管理员' })
    // console.log('-----------', index, row)
}
const handleDelete = (index, row) => {
    emitter.emit('openDeleteDialog', { row })
}

const getAdmin = async () => {
    const res = await getAdminAPI({ identity: '产品管理员' })
    if (res.status && res.status == 1) return ElMessage.error('获取数据出错')
    tableData.value = res
    // console.log(res)
}

emitter.on('refreshAdminList', async () => {
    // getAdmin()
    getAdminListLength()
    tableData.value = await returnListDataAPI({ identity: '产品管理员', page: paginationData.value.currentPage - 1 })

})
const searchAdmin = async () => {
    const res = await searchUserAPI({ account: input1.value })
    // console.log(res)
    tableData.value = res
}

// 分页
const paginationData = ref({
    // 总页数
    pageCount: 1,
    // 当前页
    currentPage: 1,
})

const adminTotal = ref(0)
const getAdminListLength = async () => {
    const res = await getAdminListLengthAPI({ identity: '产品管理员' })
    adminTotal.value = res.length
    // 每页显示10条数据,所以除以10
    paginationData.value.pageCount = Math.ceil(res.length / 10)
}

// 默认获取第一页的数据
const getFirstPageList = async () => {
    tableData.value = await returnListDataAPI({ identity: '产品管理员', page: 0 })
}
const currentPageChange = async (val) => {
    // console.log(val)
    paginationData.value.currentPage = val
    tableData.value = await returnListDataAPI({ identity: '产品管理员', page: val - 1 })
}
onMounted(() => {
    // getAdmin()
    getAdminListLength()
    getFirstPageList()
})
onBeforeUnmount(() => {
    emitter.all.clear()
})
</script>

<style lang="scss" scoped></style>

代码挺长,我们只要关注表格和分页就行了

表格绑定的数据是tableData,注意,这里已经不是全部的数据了,而是当前页的数据

分页组件:

<el-pagination :page-size="10" :current-page="paginationData.currentPage" :pager-count="5" :total="adminTotal"
   :page-count="paginationData.pageCount" @current-change="currentPageChange" layout="prev, pager, next" />

数据的总条目adminTotal根据后端的第一个接口获取,写了total属性page-count就可以不用写了

属性就不再详细介绍了,就关注current-change相关的方法

const currentPageChange = async (val) => {
    // console.log(val)
    paginationData.value.currentPage = val
    tableData.value = await returnListDataAPI({ identity: '产品管理员', page: val - 1 })
}

在前端分页的时候介绍了,current-change事件传递默认参数为当前页码,这个页码是需要给后端传递的非常重要的一个参数,实际传递给后端的page是当前页码-1后的值

还有一个需要注意的事,组件挂载时,应该默认显示第一页的数据,所以还需要写一个获取第一页数据的方法,即:

// 默认获取第一页的数据
const getFirstPageList = async () => {
    tableData.value = await returnListDataAPI({ identity: '产品管理员', page: 0 })
}

至此,后端分页的全部逻辑就完了

这玩意儿,还得常写常练,一段时间不写,直接忘光。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/247076.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言:指针与数组易错辨析

前言&#xff1a; 在学校学习指针和数组的联系时&#xff0c;对指针与数组的结合产生了很大的疑惑&#xff0c;后来不断查找资料&#xff0c;本人对指针与数组的综合有了一定的理解&#xff0c;现进行综合讨论辨析 数组指针&#xff1a; 数组指针&#xff0c;即为指向数组类…

【Pytorch】Transposed Convolution

文章目录 1 卷积2 反/逆卷积3 MaxUnpool / ConvTranspose4 encoder-decoder5 可视化 学习参考来自&#xff1a; 详解逆卷积操作–Up-sampling with Transposed Convolution PyTorch使用记录 https://github.com/naokishibuya/deep-learning/blob/master/python/transposed_co…

【改进YOLOv8】车辆测距预警系统:融合空间和通道重建卷积SCConv改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义&#xff1a; 随着交通工具的普及和道路交通的不断增加&#xff0c;车辆安全问题日益凸显。特别是在高速公路等高速道路上&#xff0c;车辆之间的距离和速度差异较…

java-两个列表进行比较,判断那些是需要新增的、删除的、和更新的

文章目录 前言两个列表进行比较&#xff0c;判断那些是需要新增的、删除的、和更新的 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实…

计算机网络编程 | 并发服务器代码实现(多进程/多线程)

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

深入了解空号检测API:提升通信效率的关键

引言 随着通信技术的不断发展&#xff0c;人们对于通信效率的要求也越来越高。在通信过程中&#xff0c;空号检测是一个非常重要的环节&#xff0c;它可以帮助我们避免无效的通信&#xff0c;提高通信效率。而空号检测API则是实现空号检测功能的重要工具。 空号检测API 空号…

Seata Server与Nacos Server搭建(商城7)

一、Nacos简介 &#xff08;一&#xff09;Nacos是什么 1、Nacos是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;相较之下&#xff0c;它更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 2、Nacos 帮助您发现、配置和管理微服务。Nacos…

【seata】 seata整合nacos + springcloud alibaba 真保姆级教程 解决各种坑点

前言&#xff1a; 坑点太多了&#xff0c;以至于需要单独写篇博客记录一下。 网上教程五花八门且不声明版本&#xff0c;文档不对应以及seata本身的bug&#xff0c;就造成了部署时各种踩坑&#xff0c;如果你和博主一样&#xff0c;已经又恰好很久没碰过nacos了&#xff0c;那可…

pandas读取Excel表指定数值 计算总和

题目要求&#xff1a;在一个文件夹里面有424个Excel表格&#xff0c;每个表格中都是统一的&#xff0c;如下图。要求计算所有表格中金额的总和。 上代码&#xff1a; import os import glob import pandas as pd# 指定文件夹路径 folder_path C:\\Users\\Administrator\\Desk…

Java—基于SpringBootWeb的综合小案例(智能学习辅助系统)

简介&#xff1a;这篇帖子是小编在看哔哩哔哩网课是的一个小案例&#xff0c;来自于黑马程序员&#xff0c;我觉得他们的课讲的很好&#xff0c;而且这个案例很有实用性&#xff0c;就在看视频的基础上&#xff0c;边温故知新&#xff0c;边实现了这个代码&#xff0c;在一些重…

Three.js中文网1-12入门案例

Three.js中文网 <template><div id"webgl"></div> </template><script setup> import * as THREE from three; import { OrbitControls } from three/addons/controls/OrbitControls.js;// 创建3D场景对象Scene const scene new THR…

【Axure RP9】动态面板使用------案例:包括轮播图和多方式登入及左侧菜单栏案例

目录 一 动态面板简介 1.1 动态面板是什么 二 轮播图 2.1 轮播图是什么 2.2 轮播图应用场景 2.3 制作实播图 三 多方式登入 3.1多方式登入是什么 3.3 多方式登入实现 四 左侧菜单栏 4.1左侧菜单栏是什么 4.2 左侧菜单栏实现 一 动态面板简介 1.1 动态面板是什么…

配置VRRP负载分担示例

一、组网需求&#xff1a; HostA和HostC通过Switch双归属到SwitchA和SwitchB。为减轻SwitchA上数据流量的承载压力&#xff0c;HostA以SwitchA为默认网关接入Internet&#xff0c;SwitchB作为备份网关&#xff1b;HostC以SwitchB为默认网关接入Internet&#xff0c;SwitchA作为…

【教程】从零开始的ORB-SLAM3的安装与配置

引言 最近项目需求需要接触vslam&#xff0c;博主选择从ORB-SLAM3下手并且记录下安装的基本流程。不得不说&#xff0c;这安装流程就像二大娘的裹脚布。 大致环境前提&#xff1a;Ubuntu20.04 一、ORB-SLAM3的源码下载 1、首先&#xff0c;为了方便管理文件&#xff0c;我们…

【infiniband】ibdump抓包

ibdump用于捕获和转储InfiniBand网络的流量。 这种工具通常用于调试和分析InfiniBand网络问题&#xff0c;包括性能瓶颈和配置错误。ibdump工具在Mellanox InfiniBand环境中较为常用&#xff0c;现由NVIDIA提供支持。 使用ibdump的基本步骤 请注意&#xff0c;您需要在安装了…

MATLAB六轴机械臂机器人的动力学分析

1、概述 动力学以牛顿第二定律为核心&#xff0c;这个定律指出了力、加速度、质量三者间的关系。 质点动力学有两类基本问题&#xff1a; 一是已知作用于质点上的力&#xff0c;求质点的运动&#xff0c;这个就是正动力学。 二是已知质点的运动&#xff0c;求作用于质点上的力…

SpringMVC-02

SpringMVC-02 SSM 框架集成与测试环境配置添加源代码 SSM 框架集成与测试 环境配置 IDEA 下创建项⽬ 创建Maven对应的Web项⽬ 配置 pom.xml 2.1. 修改 JDK 版本 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><mav…

【Matlab】如何使用MATLAB可视化二重积分(附完整MATLAB代码)

可视化二重积分 前言正文完整代码代码实现可视化结果 前言 二重积分是指在二维空间中对函数进行积分。二重积分的公式如下&#xff1a; ∫ a b ∫ c d f ( x , y ) d x d y ∫_a^b ∫_c^d f(x, y) dx dy ∫ab​∫cd​f(x,y)dxdy 其中&#xff0c; a a a 和 b b b 是 x x x…

maven打包插件maven-jar-plugin与spring-boot-maven-plugin

maven几种打包插件介绍 文章目录 &#x1f50a;1.spring-boot-maven-plugin打包后效果 &#x1f4d5;2.maven-jar-plugin打包后效果&#x1f58a;️最后总结 &#x1f50a;1.spring-boot-maven-plugin <plugins><plugin><groupId>org.springframework.boot&…

每日一题SQL

以下题目来源微信公众号【SQL数据库开发】 1、编写一个 SQL 查询来实现分数排名。如果两个分数相同&#xff0c;则两个分数排名&#xff08;Rank&#xff09;相同。请注意&#xff0c;平分后的下一个名次应该是下一个连续的整数值。换句话说&#xff0c;名次之间不应该有“间隔…