旅游网站前台模板,全国招商加盟网免费,河源建设工程交易中心网站,cms系统中ElementPlus中的分页逻辑与实现
分页是web开发中必不可少的组件#xff0c;element团队提供了简洁美观的分页组件#xff0c;配合table数据可以实现即插即用的分页效果。分页的实现可以分成两种#xff0c;一是前端分页#xff0c;二是后端分页。这两种分页分别适用于不同…ElementPlus中的分页逻辑与实现
分页是web开发中必不可少的组件element团队提供了简洁美观的分页组件配合table数据可以实现即插即用的分页效果。分页的实现可以分成两种一是前端分页二是后端分页。这两种分页分别适用于不同的业务场景分页写了无数回了但一直记不住因此记录一下这两种分页效果的逻辑和实现。
一、前端分页
前端分页适用于数据量少的情况向后端发起一次数据请求前端处理分页。优点就是接口请求少逻辑很简单缺点很明显处理大量数据时效率极低。
前端分页是我非常喜欢的一种分页模式最主要的原因就是代码简单。
现在来看看是怎么实现的。
首先看看最终的实现效果 配合代码来看
templatediv classouterel-table :datacurrentTableData height480 stripe border classtableel-table-column v-for(item, index) in tableForm :keyindex :propitem.prop :labelitem.label:width100 show-overflow-tooltip/el-table-columnel-table-column fixedright label详细 width100template #defaultscopeel-button link typeprimary sizesmall clickhandleClick(scope.$index, scope.row)查看/el-button/template/el-table-column/el-tableel-pagination classpagination small background layoutprev, pager, next :totaltotalItemsv-model:current-pagecurrentPage current-changehandelCurrentChange :hide-on-single-pagepaginationShowstylemargin-top: 20px; //div
/templatescript 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 1totalItems.value tableData.value.lengthcurrentTableData.value tableData.value.filter((item, index) index pageSize)// paginationShow.value tableData.value.length 10 ? true : false
})const handelCurrentChange page {currentPage.value page// currentPage.value 1const index pageSize * (page - 1)const nums pageSize * pageconst 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()
})
/scriptstyle langscss scoped/style首先解释一下代码
表格中的全部数据绑定的是tableData获取tableData的方法是getData在组件挂载前即调用当前页面的表格数据是currentTableData表格的表头是tableForm这里根据自己的实际情况去写
接着看分页
el-pagination classpagination small background layoutprev, pager, next :totaltotalItemsv-model:current-pagecurrentPage current-changehandelCurrentChange :hide-on-single-pagepaginationShowstylemargin-top: 20px; /参数非常多我们直接看elementplus给的api
属性名说明类型默认值small是否使用小型分页样式booleanfalsebackground是否为分页按钮添加背景色booleanfalsepage-size / v-model:page-size每页显示条目个数number—default-page-size每页默认的条目个数不设置时默认为10number—total总条目数number—page-count总页数 total 和 page-count 设置任意一个就可以达到显示页码的功能如果要支持 page-sizes 的更改则需要使用 total 属性number—pager-count设置最大页码按钮数。 页码按钮的数量当总页数超过该值时会折叠number7current-page / v-model:current-page当前页数number—default-current-page当前页数的默认初始值不设置时默认为 1number—layout组件布局子组件名用逗号分隔stringprev, pager, next, jumper, -, totalpage-sizes每页显示个数选择器的选项设置object[10, 20, 30, 40, 50, 100]popper-class每页显示个数选择器的下拉框类名string‘’prev-text替代图标显示的上一页文字string‘’prev-icon上一页的图标 比 prev-text 优先级更高string / ComponentArrowLeftnext-text替代图标显示的下一页文字string‘’next-icon下一页的图标 比 next-text 优先级更低string / ComponentArrowRightdisabled是否禁用分页booleanfalseteleported 2.3.13是否将下拉菜单teleport至 bodybooleantruehide-on-single-page只有一页时是否隐藏booleanfalse
有这么几个参数很重要
const currentPage ref(1)绑定的属性是current-page / v-model:current-page即当前页码默认为1const totalItems ref(0)绑定的属性是total就是数据总数根据tableData的长度来确定
还有一个事件很重要即current-change就是当前页码发生变化的时候执行的事件绑定的方法是handelCurrentChange来看看这个方法做了什么事情
const handelCurrentChange page {currentPage.value page// currentPage.value 1const index pageSize * (page - 1)const nums pageSize * pageconst tables []for (let i index; i nums; i) {if (tableData.value[i]) tables.push(tableData.value[i])}currentTableData.value tables
}首先这个方法接收一个默认的参数page其实就是当前的页码把默认参数赋值给currentPage获取当前页数据的起始索引即index pageSize * (page - 1)因为页码是从1开始第一页的数据从第0条开始所以其实索引应该是pageSize * (page - 1)这里的pageSize 就是每页数据要显示的条数获取当前页最后一条数据的索引即nums pageSize * page根据index和nums来获取当前页的数据
注意看我还写了一个监听事件
watch(tableData, (newVal, oldVal) {currentPage.value 1totalItems.value tableData.value.lengthcurrentTableData.value tableData.value.filter((item, index) index pageSize)// paginationShow.value tableData.value.length 10 ? true : false
})这个代码写在这里是因为我还对数据做了筛选筛选数据后tableData发生变化所以分页的总数和当前页都需要变化这和我这里讲的前端分页关系不大
至此就实现了前端分页的全部效果还是挺简单的总结一下核心在于
定义分页的参数向后端获取总的数据写好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页的数据那么前端实际的page3传递给后端实际的pagepage-12这里的逻辑要理解一下那么第3页的数据应该是索引20-29的数据这里的number20offset 20的意思就是从第20条开始取数据
后端的逻辑就是这样
2.前端代码
直接上代码
templateBreadCrumb refbreadCrumb :itemitem/BreadCrumbdiv classtable-wrappeddiv classtable-topdiv classtable-headerdiv classsearch-wrapped styledisplay: flexel-input v-modelinput1 classw-50 m-2 placeholder输入账号搜索 :prefix-iconSearchchangesearchAdmin /!-- el-button typeprimary clickgetAdmin stylemargin-left: 10px; circle :iconRefreshtitle重置列表/el-button --/divdiv classbutton-wrappedel-button typeprimary clickcreate添加产品管理员/el-button/div/divdiv classtable-contentel-table :datatableData border stylewidth: 100%el-table-column typeindex width50 /el-table-column propaccount label账号 /el-table-column propname label姓名 /el-table-column propsex label性别 /el-table-column propdepartment label部门 /el-table-column propemail label邮箱 /el-table-column propupdate_time label更新时间 /el-table-column label操作 width150template #defaultscopeel-button typesuccess sizesmallclickhandleEdit(scope.$index, scope.row)编辑/el-buttonel-button typedanger sizesmallclickhandleDelete(scope.$index, scope.row)删除/el-button/template/el-table-column/el-table/div/divdiv classtable-footerel-pagination :page-size2 :current-pagepaginationData.currentPage :pager-count5 :totaladminTotal:page-countpaginationData.pageCount current-changecurrentPageChange layoutprev, pager, next //div/divCreateAdmin/CreateAdminEditAdmin/EditAdminDeleteAdmin/DeleteAdmin
/templatescript 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 mittconst 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条数据所以除以10paginationData.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 valtableData.value await returnListDataAPI({ identity: 产品管理员, page: val - 1 })
}
onMounted(() {// getAdmin()getAdminListLength()getFirstPageList()
})
onBeforeUnmount(() {emitter.all.clear()
})
/scriptstyle langscss scoped/style代码挺长我们只要关注表格和分页就行了
表格绑定的数据是tableData注意这里已经不是全部的数据了而是当前页的数据
分页组件
el-pagination :page-size10 :current-pagepaginationData.currentPage :pager-count5 :totaladminTotal:page-countpaginationData.pageCount current-changecurrentPageChange layoutprev, pager, next /数据的总条目adminTotal根据后端的第一个接口获取写了total属性page-count就可以不用写了
属性就不再详细介绍了就关注current-change相关的方法
const currentPageChange async (val) {// console.log(val)paginationData.value.currentPage valtableData.value await returnListDataAPI({ identity: 产品管理员, page: val - 1 })
}在前端分页的时候介绍了current-change事件传递默认参数为当前页码这个页码是需要给后端传递的非常重要的一个参数实际传递给后端的page是当前页码-1后的值
还有一个需要注意的事组件挂载时应该默认显示第一页的数据所以还需要写一个获取第一页数据的方法即
// 默认获取第一页的数据
const getFirstPageList async () {tableData.value await returnListDataAPI({ identity: 产品管理员, page: 0 })
}至此后端分页的全部逻辑就完了
这玩意儿还得常写常练一段时间不写直接忘光。。。。