ElementPlus中的分页逻辑与实现

news2024/11/25 7:09:00

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 })
}

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c/c++ 文件操作(2)

文件操作读和写 顺序读写 1、fgetc、fputc 函数功能fgetc字符输入函数----->对应打开方式是 “r”fputc字符输出函数-----> 对应打开方式是 “w” 2、fgets、fputs 函数功能fgets文本行输入函数------> 对应打开方式是"r"fputs文本行输出函数------>…

社交网络分析2(上):社交网络情感分析的方法、挑战与前沿技术

社交网络分析2&#xff08;上&#xff09;&#xff1a;社交网络情感分析的方法、挑战与前沿技术 写在最前面1. 情感分析的基本概念社交网络情感分析的挑战与应用 情感分析研究现状2. 根据分析的对象&#xff0c;情感分析可以划分为哪几种类型&#xff0c;简要地进行说明。词汇表…

如何测试和挑选 2024 年最佳 Mac 数据恢复软件

数据是无价的。有些具有货币价值&#xff0c;例如您的银行帐户详细信息或官方文件。其他的则具有情感价值且不可替代&#xff0c;例如家庭照片。所有这些都存储在您的硬盘中&#xff0c;任何事情都可能出错。它可能会遇到技术错误&#xff0c;例如恶意软件攻击或驱动器故障&…

认知觉醒(七)

认知觉醒(七) 第三章 元认知——人类的终极能能力 第一节 元认知&#xff1a;成长慢&#xff0c;是因为你不会“飞” 1946年10月24日&#xff0c;一群科学家为了研究太阳的紫外线&#xff0c;在美国新墨西哥州白沙导弹试验场发射了当时世界上最先进的V2液体火箭&#xff0…

RealSense最简单配置环境只需要5分钟,不用自行添加任何包含目录、库目录。Visual Studio2022、C++。

又开始搞点云了&#xff0c;现在用的是Intel的realsense。 看网上步骤都挺繁琐的&#xff0c;本文搭建只需要5分钟。直接用官方提供的属性表&#xff0c;不用自行添加任何包含目录、库目录。 第一分钟&#xff1a;用Visual Studio新建一个工程&#xff08;此时你是没有intel.…

机器学习算法---分类

当然&#xff0c;让我为您提供更详细的机器学习算法介绍&#xff0c;重点在于每种算法的原理、优缺点&#xff0c;并在注意事项中特别提到它们对非平衡数据和高维稀疏数据的适应性。 1. 决策树&#xff08;Decision Trees&#xff09; 原理&#xff1a; 决策树通过学习简单的…

TCP/UDP 协议

目录 一.TCP协议 1.介绍 2.报文格式 ​编辑 确认号 控制位 窗口大小 3.TCP特性 二.TCP协议的三次握手 1.tcp 三次握手的过程 三.四次挥手 2.有限状态机 四.tcp协议和udp协议的区别 五.udp协议 UDP特性 六.telnet协议 一.TCP协议 1.介绍 TCP&#xff08;Transm…

深入学习 C++编程,数据结构与算法关系

数据结构是计算机科学中非常重要的概念之一。它是一种组织和存储数据的方式&#xff0c;能够有效地操作和管理数据&#xff0c;以便提高算法的效率。 以下是一些为什么要有数据结构的原因&#xff1a; (1) 数据组织&#xff1a;数据结构可以帮助我们组织和管理大量的数据。通过…

SOLIDWORKS PDM—邮件信息系统

SOLIDWORKS产品数据管理 (PDM) 解决方案可帮助您控制设计数据&#xff0c;并且从本质上改进您的团队就产品开发进行管理和协作的方式。使用 SOLIDWORKS PDM Professional&#xff0c;您的团队能够&#xff1a;1. 安全地存储和索引设计数据以实现快速检索&#xff1b;2. 打消关于…

java项目 出现同名不同类的解决方法(万能)

目录 前言1. 问题所示2. 原理分析3. 解决方法4. 补充前言 该问题尤为复杂,特别是对现成项目进行编辑,环境尤为繁琐!(不过新手也常见) 下面以自身bug为例,困惑了几天~ 主要讲解排查的方式以及根源! 1. 问题所示 先以看故事的形式看这篇文章,会对你有感而发 在A类中…

mysql 与mssql 命令有那些区别

use databasename 进入指定数据库名 命令一致 select databse() 查询当前进入数据库的名 mssql无法使用&#xff0c;mysql正常 mssql 暂无 C知道介绍 以下是MySQL和MSSQL命令的一些区别&#xff1a; 1. 连接数据库的命令不同&#xff1a; - MySQL&#xff1a;…

docker 安装keepalived

docker 安装keepalived 1.Keepalived 简介 Keepalived 是 Linux 下一个轻量级别的高可用解决方案。高可用(High Avalilability,HA)&#xff0c;其实两种不同的含义&#xff1a;广义来讲&#xff0c;是指整个系统的高可用行&#xff0c;狭义的来讲就是之主机的冗余和接管&…

56.微服务面试篇

目录 一、SpringCloud常见组件有哪些&#xff1f; 二、Nacos源码分析和Sentinel源码分析。 三、Nacos的服务注册表结构是怎样的&#xff1f; 四、Nacos如何支撑数十万服务注册压力&#xff1f; 五、Nacos如何避免并发读写冲突问题&#xff1f; 六、Nacos与Eureka的区别有…

Redis核心知识小结

基础 redis为什么快呢&#xff1f; 单线程基于io多路复用底层C语言对数据结构做了优化完全内存的操作 Redis6.0使用多线程是怎么回事? Redis不是说用单线程的吗&#xff1f;怎么6.0成了多线程的&#xff1f; Redis6.0的多线程是用多线程来处理数据的读写和协议解析&#x…

段错误详细解读

一、摘要 段错误&#xff08;Segmentation Fault&#xff09;是在编程中常见的错误之一&#xff0c;通常会导致程序崩溃。常出现在Linux系统当中&#xff0c;而且目前关于这方面的解决教程较少。 什么人会使用Linux&#xff1f;Linux 是世界上最受欢迎的操作系统之一&#xf…

产品入门第四讲:Axure动态面板

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​​​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Axure》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还…

TrustZone之强制隔离

TrustZone有时被称为一个强制执行的保护系统。请求者表示其访问的安全性,而内存系统决定是否允许该访问。内存系统基于何种方式进行检查呢? 在大多数现代系统中,内存系统的检查是由互连完成的。例如,Arm NIC-400允许系统设计人员为每个连接的完成者指定以下内容: • 安全…

ShenYu网关Http服务探活解析

文章目录 网关端服务探活admin端服务探活 Shenyu HTTP服务探活是一种用于检测HTTP服务是否正常运行的机制。它通过建立Socket连接来判断服务是否可用。当服务不可用时&#xff0c;将服务从可用列表中移除。 网关端服务探活 以divide插件为例&#xff0c;看下divide插件是如何获…

实现进程间的通信

本例程是开发一款能实现进程通信的DLL。本例程以Visual Studio 2015为例。在Visual Studio 2013&#xff0c;Visual Studio 2017都是可以。 第一步&#xff1a;在Visual Studio 2015中&#xff0c;创建DLL工程。如何创建DL&#xff0c;在这里就不作具体说明了。百度都有许多创建…

【九】python模板方法模式

9.1 模板方法模式概述 模板方法模式是一种行为设计模式&#xff0c;它使用一个抽象的基类定义了一个操作中的算法的骨架&#xff0c;而将一些步骤的实现延迟到子类中。模板方法模式允许子类在不改变算法结构的情况下重新定义算法中的某些步骤。 9.2 代码示例 在Python中使用…