分页器是非常常用的功能,网上也有很多开源的组件,但这些分页器组件有时会因为功能太全而不易于二次开发,于是写了个基础分页器留作备用。

组件逻辑

分页逻辑采用仅显示前后较少页码的形式,不显示最大页码,以防止出现大范围跳页,导致页面卡死。

<template>
  <div class="m-pagination">
    <button type="button" class="m-btn" :disabled="disablePrev" @click="handleClickPrev">上一页</button>
    <ul class="m-pager">
      <li class="m-pager-item" :class="{'is-active':page===currentPage}" v-for="page in pagers" :key="page"
          @click="handleClickPager(page)">{{page}}
      </li>
    </ul>
    <button type="button" class="m-btn" :disabled="disableNext" @click="handleClickNext">下一页
    </button>
  </div>
</template>

<script setup>
  import {defineProps, defineEmits, computed} from "vue";

  const props = defineProps({
    currentPage: {
      type: Number,
      validator(value) {
        return value > 0 && Number.isInteger(value);
      }
    },
    totalPages: {
      type: Number,
      validator(value) {
        return value > 0;
      }
    },
    pagerCount: {
      type: Number,
      validator(value) {
        return value >= 5 && value <= 11 && value % 2 !== 0;
      },
      default: 5
    }
  })

  const emit = defineEmits(["current-change", "update:current-page"]);

  const pagers = computed(() => {
    const {currentPage, totalPages, pagerCount} = props;
    const array = [];
    let start, end;
    if (totalPages <= pagerCount) {
      start = 1;
      end = totalPages + 1;
    } else {
      const offset = (pagerCount - 1) / 2;
      start = currentPage - offset;
      if (start <= 1) {
        start = 1;
        end = pagerCount + 1;
      } else if (start < offset) {
        end = start + pagerCount;
      } else if (currentPage + offset >= totalPages) {
        end = totalPages + 1;
        start = end - pagerCount;
      } else {
        end = currentPage + offset + 1;
      }
    }
    for (let i = start; i < end; i++) {
      array.push(i)
    }
    return array;
  })

  const disablePrev = computed(() => {
    return props.currentPage <= 1
  })

  const disableNext = computed(() => {
    return props.currentPage >= props.totalPages;
  })

  const handleClickPager = (page) => {
    emitter(page);
  }

  const handleClickPrev = () => {
    if (props.currentPage > 1) {
      emitter(props.currentPage - 1);
    }
  }

  const handleClickNext = () => {
    if (props.currentPage < props.totalPages) {
      emitter(props.currentPage + 1);
    }
  }

  const emitter = (page) => {
    emit("update:current-page", page);
    emit("current-change", page);
  }
</script>

<style scoped lang="scss">
  .m-pagination {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .m-pager {
    padding: 0;
    margin: 0;
    list-style: none;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .m-pager-item {
    cursor: pointer;
    padding: 0 6px;
    height: var(--m-pagination-height);
    border: solid 1px var(--m-pagination-border-color);
    min-width: var(--m-pagination-height);
    text-align: center;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 2px;

    &:not(:last-child) {
      margin-right: 10px;
    }

    &:hover {
      border-color: var(--m-pagination-text-color);
    }

    &.is-active {
      background-color: var(--m-pagination-text-color);
      color: white;
    }
  }

  .m-btn {
    line-height: var(--m-pagination-height);
    box-sizing: border-box;
    height: var(--m-pagination-height);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    background-color: transparent;
    outline: none;
    border: solid 1px var(--m-pagination-border-color);
    border-radius: 2px;

    &:first-child {
      margin-right: 10px;
    }

    &:last-child {
      margin-left: 10px;
    }

    &:not(:disabled) {
      &:hover {
        border-color: var(--m-pagination-text-color);
        color: white;
        background-color: var(--m-pagination-text-color);
      }
    }

    &:disabled {
      cursor: not-allowed;
    }
  }
</style>

样式变量

样式变量代码最好放置在全局样式表(如 global.scss)中,便于在使用到分页器组件的内部进行覆盖。

:root {
  --m-pagination-border-color: #ccc;
  --m-pagination-text-color: #3396FD;
  --m-pagination-height: 32px;
}

效果

pagination

最近更新:
作者: MeFelixWang