Electron for OpenHarmony 实战:Pagination 分页组件实现

分页是数据展示中必不可少的功能,当数据量大的时候,一次性加载所有数据既不现实也不友好。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-textnext-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

相关推荐
程序员三藏4 小时前
接口测试及常用接口测试工具总结
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
小二·6 小时前
Python Web 开发进阶实战 :AI 原生数字孪生 —— 在 Flask + Three.js 中构建物理世界实时仿真与优化平台
前端·人工智能·python
hmywillstronger7 小时前
【Rhino】【Python】 查询指定字段并cloud标注
开发语言·python
dyyx1117 小时前
如何从Python初学者进阶为专家?
jvm·数据库·python
嗯嗯=7 小时前
STM32单片机学习篇9
stm32·单片机·学习
二十雨辰7 小时前
[python]-函数
开发语言·python
CryptoRzz8 小时前
如何高效接入日本股市实时数据?StockTV API 对接实战指南
java·python·kafka·区块链·状态模式·百度小程序
小二·8 小时前
Python Web 开发进阶实战(终章):从单体应用到 AI 原生生态 —— 45 篇技术演进全景与未来开发者生存指南
前端·人工智能·python
极客小云8 小时前
【基于 PyQt6 的红外与可见光图像配准工具开发实战】
c语言·python·yolo·目标检测
ooo-p8 小时前
FPGA学习篇——Verilog学习之“呼吸灯”
学习·fpga开发