Vue3 分页

组件

<template>
  <div :class="{ hidden: hidden }" class="pagination-container">
    <el-pagination
      :background="background"
      v-model:current-page="currentPage"
      v-model:page-size="pageSize"
      :page-sizes="pageSizes"
      :pager-count="5"
      :layout="layout"
      :total="total"
      v-bind="$attrs"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    >
      <span v-if="selected >= 0" class="text"> 已选择 {{ selected }} 条 </span>
    </el-pagination>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { scrollTo } from '@/utils/libs/scroll-to'

const props = defineProps({
  total: { type: Number, required: true },
  page: { type: Number, default: 1 },
  limit: { type: Number, default: 20 },
  pageSizes: { type: Array as PropType<Array<number>>, default: () => [10, 20, 30, 40, 50] },
  layout: { type: String, default: 'total , slot , -> ,  sizes, prev, pager, next, jumper ' },
  background: { type: Boolean, default: true },
  autoScroll: { type: Boolean, default: true },
  hidden: { type: Boolean, default: false },
  selected: { type: Number, default: 0 }
})

const childEmit = defineEmits(['update:page', 'update:limit', 'pagination'])

const currentPage = computed({
  get: () => props.page,
  set: (val) => {
    childEmit('update:page', val)
  }
})

const pageSize = computed({
  get: () => props.limit,
  set: (val) => {
    childEmit('update:limit', val)
  }
})

const handleSizeChange = (val) => {
  childEmit('pagination', { pageNum: currentPage, pageSize: val })
  if (props.autoScroll) scrollTo(0, 800)
}

const handleCurrentChange = (val) => {
  childEmit('pagination', { pageNum: val, pageSize: props.limit })
  if (props.autoScroll) scrollTo(0, 800)
}
</script>

<style lang="scss"></style>

hook

import { ref, reactive, nextTick } from 'vue'

export default function (
  ajaxRequest, // 请求接口
  multipleTable, // table ref
  paging, // 分页大小
  parameter, // 搜索参数
  multipleTableKey = '',
  isMultipleTable = false
) {
  const mixinsLoading = ref<boolean>(false)
  const tableData = ref([])
  const selectCodes = ref([]) // 选中的数据
  const originalData = ref(null) // 原始数据

  /* 集中处理筛选数据 */
  function filterFrom() {
    const para = JSON.parse(JSON.stringify(parameter.value))
    for (const key in para) {
      if (para[key] === '' || para[key] === null) delete para[key] // 如果属性为空 删除掉
    }
    parameter.value = para
    getList()
  }

  // 重新搜索
  function reload(parameters = {}) {
    paging.pageNum = 1
    parameter.value = parameters // 保证响应式
    filterFrom()
  }

  function handleCurrentChange(paging) {
    Object.assign(paging, paging)
    getList()
  }

  async function getList() {
    mixinsLoading.value = true
    parameter.value = Object.assign(parameter.value, paging)
    parameter.value.page = parameter.value.pageNum
    parameter.value.page_size = parameter.value.pageSize
    delete parameter.value.totalCount
    delete parameter.value.pageNum
    delete parameter.value.pageSize

    try {
      const { data } = await ajaxRequest(parameter.value)
      // const body = []
      originalData.value = data // 保持响应式
      tableData.value.length = 0
      for (const iterator of data?.datalist) {
        tableData.value.push(iterator)
      }

      paging.totalCount = +data?.total || 0
      if (isMultipleTable) {
        tableSelect()
      } else {
        mixinsLoading.value = false
      }
    } catch (err) {
      // console.log('err:', error)
      tableData.value.length = 0
      mixinsLoading.value = false
    }
  }

  /* 重置 */
  function reset(paramete = {}) {
    paging = reactive({ pageNum: 1, pageSize: 20, totalCount: 0 })
    for (const item in paramete) paramete[item] = ''
    parameter.value = paramete // 本地赋值
    filterFrom()
    selectCodes.value = []
  }

  // 表格勾选
  function tableSelect() {
    if (!tableData.value && !tableData.value.length) {
      mixinsLoading.value = false
      return
    }
    tableData.value.forEach((element) => {
      if (selectCodes.value.join().includes(element[multipleTableKey])) {
        nextTick(() => {
          multipleTable.value.toggleRowSelection(element, true)
        })
      }
    })
    mixinsLoading.value = false
  }

  return {
    mixinsLoading,
    selectCodes,
    originalData,
    tableData,
    handleCurrentChange,
    reload,
    reset
  }
}

param

import { ref, reactive } from 'vue'

export default function (
  tableRefVal,
  parameterVal,
  pagingVal = {
    pageNum: 1,
    pageSize: 20,
    totalCount: 0
  },
  paginationLayoutVal = 'total , slot , -> ,  sizes, prev, pager, next, jumper '
) {
  const tableRef = ref(tableRefVal) // table DOM
  const paging = reactive(pagingVal) // 分页大小
  const parameter = ref(parameterVal) // 查询参数
  const paginationLayout = paginationLayoutVal // 页码设置

  return { tableRef, paging, parameter, paginationLayout }
}

使用

<template>
  <div class="index-table pr-20px pl-20px">
    <list-pagination
      class="mt-20px"
      :total="paging.totalCount"
      v-model:page="paging.pageNum"
      v-model:limit="paging.pageSize"
      :layout="paginationLayoutVal"
      @pagination="handleCurrentChange"
    />
  </div>
</template>

<script setup lang="ts">
import ListPagination from '@/components/ListPagination/index.vue'
import useListPagination from '@/hooks/list-pagination/useListPagination'
import { onMounted } from 'vue'
import usePagParam from '@/hooks/list-pagination/usePagParam'
import { scanList } from '@/api/home'
import { TABLE_BORDER_RADIUS } from '@/config/style'
import tableHeightHook from '@/hooks/table-height/tableHeight'

const handleSelectionChange = () => {}

const { tableRef, paging, parameter } = usePagParam(null, {})
const paginationLayoutVal = 'total , -> ,  sizes, prev, pager, next, jumper '
let { mixinsLoading, tableData, handleCurrentChange, reload } = useListPagination(
  scanList,
  tableRef,
  paging,
  parameter
)

const topHeightValCom = tableHeightHook(tableRef) // 获取 table 的高度

onMounted(() => {
  reload() // 初始化数据
})

defineExpose({ reload }) // 共父组件使用
</script>

<style lang="scss" scoped>
.index-table {
  margin-top: 0;

  &__content {
    width: 100%;
    height: 200px;
  }
}

@mixin state-modify {
  height: 5px;
  width: 5px;
  border-radius: 50%;
  margin-right: 10px;
}

.modify-orange {
  @include state-modify;
  background: orange;
}

.modify-green {
  @include state-modify;
  background: green;
}
</style>
### Vue3 中实现分页的方法 在 Vue3 中,可以通过多种方式来实现分页功能。以下是两种常见的方法:一种是通过手动编写逻辑代码实现分页;另一种则是利用现有的第三方库或组件。 #### 方法一:手写分页逻辑 可以创建一个简单的分页器组件,该组件接收数据列表作为属性并显示指定数量的数据项。以下是一个基本的手动分页示例: ```vue <template> <div> <!-- 显示当前页面的内容 --> <ul> <li v-for="item in paginatedData" :key="item.id">{{ item.name }}</li> </ul> <!-- 分页按钮 --> <button @click="prevPage">上一页</button> <span>第 {{ currentPage }} / {{ totalPages }} 页</span> <button @click="nextPage">下一页</button> </div> </template> <script> import { ref, computed } from &#39;vue&#39;; export default { props: { data: { type: Array, required: true }, pageSize: { type: Number, default: 10 } }, setup(props) { const currentPage = ref(1); // 计算总页数 const totalPages = computed(() => Math.ceil(props.data.length / props.pageSize)); // 获取当前页码对应的数据子集 const paginatedData = computed(() => { const start = (currentPage.value - 1) * props.pageSize; const end = start + props.pageSize; return props.data.slice(start, end); }); // 上一页操作 function prevPage() { if (currentPage.value > 1) { currentPage.value--; } } // 下一页操作 function nextPage() { if (currentPage.value < totalPages.value) { currentPage.value++; } } return { currentPage, totalPages, paginatedData, prevPage, nextPage }; } }; </script> ``` 上述代码展示了如何基于 `props` 和响应式变量构建一个基础的分页组件[^1]。 --- #### 方法二:使用第三方分页插件 Vue 社区提供了许多成熟的分页解决方案,比如 Element Plus 或 Vuetify 的分页组件。这些工具通常已经内置了样式和交互逻辑,能够快速集成到项目中。 ##### 使用 Element Plus 实现分页 Element Plus 是 Vue3 生态中最流行的 UI 库之一,它提供了一个名为 `<el-pagination>` 的分页组件。下面是如何将其用于分页的一个简单例子: ```vue <template> <div> <!-- 数据展示区域 --> <ul> <li v-for="item in paginatedData" :key="item.id">{{ item.name }}</li> </ul> <!-- 分页控件 --> <el-pagination background layout="prev, pager, next" :total="data.length" :page-size="pageSize" :current-page="currentPage" @current-change="handlePageChange"> </el-pagination> </div> </template> <script> import { ref, computed } from &#39;vue&#39;; import { ElPagination } from &#39;element-plus&#39;; // 导入分页组件 export default { components: { ElPagination }, // 注册分页组件 props: { data: { type: Array, required: true } }, setup(props) { const currentPage = ref(1); // 当前页码 const pageSize = ref(10); // 每页条目数 // 总页数计算 const totalPages = computed(() => Math.ceil(props.data.length / pageSize.value)); // 当前页码对应的数据显示范围 const paginatedData = computed(() => { const start = (currentPage.value - 1) * pageSize.value; const end = start + pageSize.value; return props.data.slice(start, end); }); // 处理页码变化事件 function handlePageChange(pageNumber) { currentPage.value = pageNumber; // 更新当前页码 } return { currentPage, pageSize, totalPages, paginatedData, handlePageChange }; } }; </script> ``` 此代码片段演示了如何结合自定义逻辑与 Element Plus 提供的功能完成更复杂的分页需求[^2]。 --- #### 注意事项 无论是采用手工编码还是依赖于现有框架,在实际开发过程中都需要考虑以下几个方面: - **性能优化**:当处理大量数据时,应避免一次性加载全部内容至内存中。 - **用户体验设计**:合理设置每页大小以及导航按钮布局以提升易用性。 - **兼容性和可扩展性**:确保所选方案能够在不同浏览器环境下正常工作,并支持未来可能新增的需求变更。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

史一试

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值