
分页是数据展示中必不可少的功能,当数据量大的时候,一次性加载所有数据既不现实也不友好。Element Plus 的 Pagination 组件提供了完整的分页功能,支持页码切换、每页条数设置、快速跳转等。这篇文章来聊聊在 Electron for OpenHarmony 项目中如何使用 Pagination 分页组件。
Pagination 的基本结构
Pagination 组件由多个部分组成:上一页按钮、页码列表、下一页按钮,还可以添加总条数显示、每页条数选择、页码跳转等功能。通过 layout 属性可以灵活配置显示哪些部分。
基础用法
最简单的分页,只有页码和翻页按钮:
vue
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const currentPage = ref(1)
const pageSize = ref(10)
</script>
<template>
<div class="demo-page">
<div class="demo-header">
<el-button @click="router.push('/components')" :icon="'ArrowLeft'" circle />
<h2>Pagination 分页</h2>
</div>
<el-scrollbar height="calc(100vh - 80px)">
<div class="demo-content">
<el-card class="demo-card">
<template #header>基础用法</template>
<el-pagination layout="prev, pager, next" :total="50" />
</el-card>
</div>
</el-scrollbar>
</div>
</template>
layout 属性定义分页组件的布局,prev 是上一页按钮,pager 是页码列表,next 是下一页按钮。
:total 设置数据总条数,组件会根据总条数和每页条数自动计算总页数。
带背景色的分页
给页码按钮添加背景色,更容易区分:
vue
<el-card class="demo-card">
<template #header>带背景色</template>
<el-pagination background layout="prev, pager, next" :total="100" />
</el-card>
background 属性给页码按钮添加背景色,当前页会高亮显示,视觉效果更好。
小型分页
适合空间有限的场景:
vue
<el-card class="demo-card">
<template #header>小型分页</template>
<el-pagination small layout="prev, pager, next" :total="50" />
</el-card>
small 属性使用小型分页样式,按钮和文字都会变小,适合在表格底部或者侧边栏使用。
完整功能
包含所有分页功能的完整配置:
vue
<script setup lang="ts">
import { ref } from 'vue'
const currentPage = ref(1)
const pageSize = ref(10)
</script>
<template>
<el-card class="demo-card">
<template #header>完整功能</template>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="400"
/>
</el-card>
</template>
这是项目中实际使用的完整分页配置:
v-model:current-page 双向绑定当前页码,页码变化时会自动更新。
v-model:page-size 双向绑定每页条数,用户选择不同的每页条数时会更新。
:page-sizes 设置每页条数的选项,用户可以从这些选项中选择。
layout 中的各个部分:total 显示总条数、sizes 每页条数选择器、prev 上一页、pager 页码、next 下一页、jumper 页码跳转输入框。
layout 的灵活配置
layout 属性非常灵活,可以根据需要组合不同的部分:
vue
<script setup lang="ts">
import { ref } from 'vue'
const page1 = ref(1)
const page2 = ref(1)
const page3 = ref(1)
</script>
<template>
<el-space direction="vertical" style="width: 100%;">
<!-- 只有翻页按钮 -->
<el-pagination
v-model:current-page="page1"
layout="prev, next"
:total="100"
/>
<!-- 显示总数和页码 -->
<el-pagination
v-model:current-page="page2"
layout="total, prev, pager, next"
:total="100"
/>
<!-- 带跳转的简洁版 -->
<el-pagination
v-model:current-page="page3"
layout="prev, pager, next, jumper"
:total="100"
/>
</el-space>
</template>
根据实际需求选择合适的布局,数据量小的时候可以简化,数据量大的时候提供更多控制选项。
监听分页事件
响应用户的分页操作:
vue
<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const currentPage = ref(1)
const pageSize = ref(10)
const handleCurrentChange = (page: number) => {
ElMessage.info(`切换到第 ${page} 页`)
// 这里可以调用接口获取对应页的数据
fetchData(page, pageSize.value)
}
const handleSizeChange = (size: number) => {
ElMessage.info(`每页显示 ${size} 条`)
// 每页条数变化时,通常需要重置到第一页
currentPage.value = 1
fetchData(1, size)
}
const fetchData = (page: number, size: number) => {
console.log(`获取数据:第 ${page} 页,每页 ${size} 条`)
// 实际项目中这里调用 API
}
</script>
<template>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="200"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</template>
@current-change 在页码变化时触发,参数是新的页码。
@size-change 在每页条数变化时触发,参数是新的每页条数。
通常在这两个事件中调用接口获取对应的数据。
配合表格使用
分页最常见的场景是配合表格使用:
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
interface User {
id: number
name: string
email: string
status: string
}
// 模拟数据
const allData = ref<User[]>(
Array.from({ length: 95 }, (_, i) => ({
id: i + 1,
name: `用户${i + 1}`,
email: `user${i + 1}@example.com`,
status: i % 3 === 0 ? '禁用' : '正常'
}))
)
const currentPage = ref(1)
const pageSize = ref(10)
// 计算当前页的数据
const tableData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return allData.value.slice(start, end)
})
const total = computed(() => allData.value.length)
</script>
<template>
<el-card>
<template #header>用户列表</template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === '正常' ? 'success' : 'danger'">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 16px; display: flex; justify-content: flex-end;">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="total"
background
/>
</div>
</el-card>
</template>
这个例子展示了分页和表格的配合:
allData 存储所有数据,tableData 通过计算属性获取当前页的数据。
分页组件放在表格下方,通常靠右对齐。
页码或每页条数变化时,tableData 会自动重新计算,表格内容随之更新。
禁用状态
某些情况下需要禁用分页:
vue
<script setup lang="ts">
import { ref } from 'vue'
const loading = ref(false)
const currentPage = ref(1)
const fetchData = async () => {
loading.value = true
// 模拟请求
await new Promise(resolve => setTimeout(resolve, 1000))
loading.value = false
}
</script>
<template>
<div>
<el-button @click="fetchData" :loading="loading">加载数据</el-button>
<el-pagination
v-model:current-page="currentPage"
layout="prev, pager, next"
:total="100"
:disabled="loading"
style="margin-top: 16px;"
/>
</div>
</template>
:disabled 属性禁用整个分页组件,在数据加载过程中禁用分页可以防止用户重复操作。
隐藏单页
只有一页数据时隐藏分页:
vue
<script setup lang="ts">
import { ref } from 'vue'
const total = ref(8)
const pageSize = ref(10)
</script>
<template>
<el-pagination
layout="prev, pager, next"
:total="total"
:page-size="pageSize"
:hide-on-single-page="true"
/>
<p style="color: #909399; font-size: 14px;">
当前共 {{ total }} 条数据,每页 {{ pageSize }} 条,只有一页所以分页隐藏了
</p>
</template>
:hide-on-single-page="true" 在只有一页时自动隐藏分页组件,避免显示无意义的分页。
自定义上下页按钮
用图标或文字替换默认的箭头:
vue
<template>
<el-space direction="vertical" style="width: 100%;">
<!-- 使用文字 -->
<el-pagination
layout="prev, pager, next"
:total="100"
prev-text="上一页"
next-text="下一页"
/>
<!-- 使用图标 -->
<el-pagination
layout="prev, pager, next"
:total="100"
:prev-icon="'DArrowLeft'"
:next-icon="'DArrowRight'"
/>
</el-space>
</template>
prev-text 和 next-text 用文字替换箭头图标。
:prev-icon 和 :next-icon 用自定义图标替换默认箭头。
与鸿蒙原生能力结合
在 Electron for OpenHarmony 项目中,可以结合原生能力优化分页体验:
vue
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useOhos } from '@/composables/useOhos'
import { ElMessage } from 'element-plus'
const { vibrate, showNotification } = useOhos()
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(156)
const loading = ref(false)
// 模拟数据
const tableData = ref<any[]>([])
const fetchData = async (page: number, size: number) => {
loading.value = true
// 模拟网络请求
await new Promise(resolve => setTimeout(resolve, 500))
// 生成模拟数据
const start = (page - 1) * size
tableData.value = Array.from({ length: Math.min(size, total.value - start) }, (_, i) => ({
id: start + i + 1,
title: `数据项 ${start + i + 1}`,
createTime: new Date(Date.now() - Math.random() * 10000000000).toLocaleDateString()
}))
loading.value = false
// 震动反馈
await vibrate(50)
}
// 页码变化时获取数据
watch([currentPage, pageSize], ([page, size]) => {
fetchData(page, size)
}, { immediate: true })
// 到达最后一页时提醒
watch(currentPage, async (page) => {
const totalPages = Math.ceil(total.value / pageSize.value)
if (page === totalPages) {
await showNotification('已到最后一页', `共 ${total.value} 条数据`)
}
})
const handleCurrentChange = (page: number) => {
ElMessage.info(`第 ${page} 页`)
}
</script>
<template>
<el-card>
<template #header>数据列表</template>
<el-table :data="tableData" v-loading="loading" style="width: 100%">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="title" label="标题" />
<el-table-column prop="createTime" label="创建时间" width="120" />
</el-table>
<div style="margin-top: 16px; display: flex; justify-content: space-between; align-items: center;">
<span style="color: #909399; font-size: 14px;">
第 {{ currentPage }} 页,共 {{ Math.ceil(total / pageSize) }} 页
</span>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="sizes, prev, pager, next, jumper"
:total="total"
:disabled="loading"
background
@current-change="handleCurrentChange"
/>
</div>
</el-card>
</template>
这个例子展示了如何结合原生能力:
翻页时触发轻微震动,给用户触觉反馈;到达最后一页时发送系统通知提醒用户;加载数据时禁用分页防止重复操作。
无限滚动替代方案
有时候无限滚动比分页更适合移动端:
vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
const list = ref<any[]>([])
const loading = ref(false)
const noMore = ref(false)
const page = ref(1)
const pageSize = 20
const total = 85
const loadMore = async () => {
if (loading.value || noMore.value) return
loading.value = true
// 模拟请求
await new Promise(resolve => setTimeout(resolve, 500))
const start = (page.value - 1) * pageSize
const newItems = Array.from(
{ length: Math.min(pageSize, total - start) },
(_, i) => ({
id: start + i + 1,
content: `列表项 ${start + i + 1}`
})
)
list.value.push(...newItems)
page.value++
if (list.value.length >= total) {
noMore.value = true
ElMessage.info('已加载全部数据')
}
loading.value = false
}
onMounted(() => {
loadMore()
})
</script>
<template>
<el-card>
<template #header>无限滚动</template>
<div
v-infinite-scroll="loadMore"
:infinite-scroll-disabled="loading || noMore"
:infinite-scroll-distance="50"
style="height: 300px; overflow-y: auto;"
>
<div
v-for="item in list"
:key="item.id"
style="padding: 12px; border-bottom: 1px solid #ebeef5;"
>
{{ item.content }}
</div>
<p v-if="loading" style="text-align: center; color: #909399; padding: 12px;">
加载中...
</p>
<p v-if="noMore" style="text-align: center; color: #909399; padding: 12px;">
没有更多了
</p>
</div>
</el-card>
</template>
v-infinite-scroll 指令实现无限滚动,滚动到底部时自动加载更多数据。
这种方式在移动端体验更好,用户不需要点击翻页按钮。
分页状态持久化
保存分页状态,刷新页面后恢复:
vue
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
const STORAGE_KEY = 'pagination_state'
const currentPage = ref(1)
const pageSize = ref(10)
// 从本地存储恢复状态
onMounted(() => {
const saved = localStorage.getItem(STORAGE_KEY)
if (saved) {
try {
const state = JSON.parse(saved)
currentPage.value = state.page || 1
pageSize.value = state.size || 10
} catch (e) {
console.error('恢复分页状态失败', e)
}
}
})
// 保存状态到本地存储
watch([currentPage, pageSize], ([page, size]) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ page, size }))
})
</script>
<template>
<el-card>
<template #header>分页状态持久化</template>
<p style="margin-bottom: 16px; color: #606266;">
当前页码和每页条数会保存到本地,刷新页面后自动恢复
</p>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
:total="500"
background
/>
</el-card>
</template>
通过 localStorage 保存分页状态,用户刷新页面或重新打开应用后可以恢复到之前的位置。
URL 参数同步
把分页状态同步到 URL,方便分享和书签:
vue
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const currentPage = ref(1)
const pageSize = ref(10)
// 从 URL 参数初始化
onMounted(() => {
const page = parseInt(route.query.page as string)
const size = parseInt(route.query.size as string)
if (!isNaN(page) && page > 0) currentPage.value = page
if (!isNaN(size) && size > 0) pageSize.value = size
})
// 同步到 URL
watch([currentPage, pageSize], ([page, size]) => {
router.replace({
query: {
...route.query,
page: page.toString(),
size: size.toString()
}
})
})
</script>
<template>
<el-card>
<template #header>URL 参数同步</template>
<p style="margin-bottom: 16px; color: #606266;">
分页状态会同步到 URL 参数,可以直接分享链接
</p>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="200"
background
/>
</el-card>
</template>
分页状态同步到 URL 后,用户可以直接分享带有分页参数的链接,其他人打开后会显示相同的页面。
样式定制
vue
<style scoped>
.demo-page {
padding: 20px;
background: #f5f7fa;
height: 100vh;
box-sizing: border-box;
}
.demo-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
}
.demo-header h2 {
margin: 0;
}
.demo-content {
display: flex;
flex-direction: column;
gap: 16px;
padding-bottom: 20px;
}
/* 自定义分页样式 */
:deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
background-color: #409eff;
}
:deep(.el-pagination.is-background .el-pager li:not(.is-disabled):hover) {
color: #409eff;
}
</style>
通过 :deep() 可以自定义分页组件的样式,比如修改选中页码的背景色、悬停效果等。
小结
Pagination 分页组件是数据展示的重要组件,这篇文章介绍了它的各种用法:基础分页、带背景色、小型分页、完整功能、layout 配置、事件监听、配合表格使用、禁用状态、隐藏单页、自定义按钮,以及与鸿蒙原生能力的结合。Pagination 组件的核心是通过 layout 灵活配置显示的部分,通过 v-model 双向绑定页码和每页条数,通过事件监听响应用户操作。在 Electron for OpenHarmony 项目中,结合原生的震动和通知能力,可以提供更好的分页交互体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net