Lógica de paginación e implementación en ElementPlus
La paginación es un componente esencial en el desarrollo web. El equipo de elementos proporciona componentes de paginación simples y hermosos, que se pueden usar con datos de tablas para lograr efectos de paginación plug-and-play. La implementación de la paginación se puede dividir en dos tipos, una es la paginación de front-end y la otra es la paginación de back-end. Estos dos tipos de paginación son adecuados para diferentes escenarios comerciales, respectivamente. He escrito sobre la paginación innumerables veces, pero nunca he podido recordarlo, por lo que registraré la lógica y la implementación de estos dos efectos de paginación.
1. Paginación frontal
La paginación front-end es adecuada para situaciones donde la cantidad de datos es pequeña: se inicia una solicitud de datos al back-end y el front-end maneja la paginación. La ventaja es que hay pocas solicitudes de interfaz y la lógica es muy simple, pero la desventaja es obvia: cuando se procesan grandes cantidades de datos, la eficiencia es extremadamente baja.
La paginación front-end es un modo de paginación que me gusta mucho, la razón principal es que el código es simple.
Ahora veamos cómo se logra esto.
Primero, echemos un vistazo a la implementación final:
Veámoslo con el código:
<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>
Primero explique el código:
- Todos los datos de la tabla están vinculados a tableData. El método para obtener tableData es getData, que se llama antes de montar el componente.
- Los datos de la tabla de la página actual son currentTableData
- El encabezado de la tabla es tableForm, escríbalo aquí de acuerdo con su situación real.
A continuación mire la paginación:
<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;" />
Hay muchos parámetros, veamos directamente la API proporcionada por elementplus:
Nombre del Atributo | ilustrar | tipo | valor por defecto |
---|---|---|---|
pequeño | Si se debe utilizar un estilo de paginación pequeño | boolean |
FALSO |
fondo | Si se debe agregar un color de fondo al botón de paginación | boolean |
FALSO |
tamaño de página / modelo v: tamaño de página | Número de elementos mostrados por página | number |
— |
tamaño de página predeterminado | El número predeterminado de entradas por página; si no se establece, el valor predeterminado es 10 | number |
— |
total | Número total de entradas | number |
— |
recuento de páginas | El número total de páginas, total y page-count se pueden configurar en cualquiera para lograr la función de mostrar números de página; si desea admitir page-sizes , necesitas usar el atributo total |
number |
— |
recuento de buscapersonas | Establezca el número máximo de botones de página. El número de botones de número de página, que se colapsarán cuando el número total de páginas supere este valor. | number |
7 |
página-actual/modelo-v:página-actual | número de página actual | number |
— |
página-actual-predeterminada | El valor inicial predeterminado del número de página actual, el valor predeterminado es 1 si no se establece | number |
— |
disposición | Diseño de componentes, nombres de subcomponentes separados por comas | string |
anterior, buscapersonas, siguiente, puente, ->, total |
tamaños de página | Mostrar la configuración de opciones del selector de números por página | object |
[10, 20, 30, 40, 50, 100] |
clase popper | El nombre de clase del cuadro desplegable del selector de números que se muestra en cada página. | string |
'' |
texto anterior | Texto de la página anterior mostrado por el icono de reemplazo | string |
'' |
icono anterior | El icono de la página anterior tiene mayor prioridad que prev-text |
string /Component |
FlechaIzquierda |
texto siguiente | Texto de la página siguiente mostrado por el icono de reemplazo | string |
'' |
icono siguiente | El icono de la página siguiente tiene una prioridad menor que next-text |
string /Component |
FlechaDerecha |
desactivado | Ya sea para deshabilitar la paginación | boolean |
FALSO |
teletransportado 2.3.13 | Ya sea para teletransportar el menú desplegable al cuerpo | boolean |
verdadero |
ocultar en una sola página | Si se debe ocultar cuando solo hay una página | boolean |
FALSO |
Hay varios parámetros que son muy importantes:
const currentPage = ref(1)
, el atributo vinculado es página actual / modelo v: página actual, que es el número de página actual y el valor predeterminado es 1const totalItems = ref(0)
, el atributo vinculado es total, que es el número total de datos, determinado según la longitud de tableData
Hay otro evento muy importante, a sabercurrent-change
, que es el evento que se ejecuta cuando cambia el número de página actual. El método de enlace es handleCurrentChange. Veamos qué hace este método.
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
}
- Primero, este método recibe una página de parámetros predeterminada, que en realidad es el número de página actual, y asigna el parámetro predeterminado a página actual.
- Obtenga el índice inicial de los datos de la página actual, es decir, índice = tamaño de página * (página - 1). Debido a que el número de página comienza desde 1 y los datos de la primera página comienzan desde el elemento 0, el índice en realidad debería ser tamaño de página * ( página - 1), tamaño de página aquí es el número de elementos de datos que se mostrarán en cada página.
- Obtenga el índice del último dato en la página actual, es decir, nums = pageSize * page
- Obtenga los datos de la página actual según el índice y los números
Tenga en cuenta que también escribí un evento de escucha:
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
})
Este código está escrito aquí porque también he filtrado los datos. Después de filtrar los datos, tableData cambia, por lo que el número total de paginaciones y la página actual deben cambiar. Esto tiene poco que ver con la paginación frontal de la que estoy hablando. aquí.
Hasta ahora se han logrado todos los efectos de la paginación front-end, lo cual es bastante simple, en resumen, el núcleo radica en:
- Definir parámetros de paginación
- Obtener datos totales del backend
- Escribe el método de cambio actual.
2. Paginación de fondo
De hecho, la paginación de back-end es la idea correcta, porque en circunstancias normales, el back-end no enviará todos los datos al front-end a la vez y la eficiencia de transmisión es baja e insegura. Pero la paginación de fondo es relativamente problemática, pase lo que pase, registrémosla.
Primero echemos un vistazo a los resultados de mi paginación:
Todos usan datos de prueba y la paginación está en la esquina inferior derecha. De hecho, no hay diferencia en la visualización, pero la lógica es completamente diferente.
1. Código de fondo
El backend ha escrito dos interfaces. Las escribí usando node. Una es para obtener el número total de listas y la otra es para monitorear el cambio de página y devolver los datos de la página actual al frontend.
El código se muestra a continuación:
// 分页
// 获取列表总数
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);
});
};
No hay nada que decir acerca de obtener el número total de listas, es solo una declaración de consulta.
Mire principalmente el método returnListData
De hecho, el front-end pasa dos parámetros al back-end, uno es el número de página actual (página) y el otro es la condición de consulta (identidad).
Mira la declaración de consulta.
const sql = `select * from users where identity = ? limit 10 offset ${
number}`;
límite 10 significa devolver los primeros 10 datos
El desplazamiento aquí es muy crítico, indica dónde comenzar a devolver 10 datos. Por ejemplo, si quiero consultar los datos en la página 3, entonces la página real=3 en el front-end,, entonces los datos de la página 3 deben ser los datos del índice 20-29, y el número aquí = 20, el desplazamiento 20 significa comenzar a recuperar datos del elemento 20 se pasa a la página real = página-1=2 en el backend (necesita comprender la lógica aquí)
La lógica del backend es así
2. Código de interfaz
Sube el código directamente
<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>
El código es bastante largo, solo debemos centrarnos en la tabla y la paginación.
Los datos vinculados a la tabla son tableData. Tenga en cuenta que estos no son todos los datos, sino los datos de la página actual.
Componente de paginación:
<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" />
La entrada total de datos adminTotal se obtiene de la primera interfaz del backend, no es necesario escribir el atributo total page-count.
Los atributos no se presentarán en detalle, pero nos centraremos en los métodos relacionados con el cambio actual.
const currentPageChange = async (val) => {
// console.log(val)
paginationData.value.currentPage = val
tableData.value = await returnListDataAPI({
identity: '产品管理员', page: val - 1 })
}
Como se introdujo durante la paginación del front-end, el parámetro predeterminado pasado por el evento de cambio actual es el número de página actual. Este número de página es un parámetro muy importante que debe pasarse al backend. La página realmente pasada al backend es la valor después del número de página actual: 1.
Otra cosa que necesita atención es que cuando se monta el componente, los datos de la primera página deben mostrarse de forma predeterminada, por lo que debe escribir un método para obtener los datos de la primera página, es decir:
// 默认获取第一页的数据
const getFirstPageList = async () => {
tableData.value = await returnListDataAPI({
identity: '产品管理员', page: 0 })
}
En este punto, toda la lógica de la paginación de fondo está completa.
Tienes que escribir y practicar estas cosas con frecuencia. Si no escribes durante un tiempo, lo olvidarás todo. . . .