Vue: 易用的分页组件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/csu_passer/article/details/89183314

我并不太喜欢直接使用UI库,因为往往不好直接修改源码来自定义。同时为了更好的理解Vue,和以前编写的各种组件一样打算自己手动实现自己项目需求中的组件–

UI

UI大体和Iview保持一致
在这里插入图片描述
组件源码复制即用,没有animated.css的项目就没有动画效果…

使用

    <Page @pageClick="handleChangePage" :total="total" :current="current" :page-size="pageSize" @pageSizeChange="handlePageSizeChange"></Page>

需要自己计算切换每页显示条目后的当前页√

    handleChangePage (index) {
      this.current = index
    },
    handlePageSizeChange (size) {
      this.current = Math.ceil(Math.ceil(this.total / size) * this.current / Math.ceil(this.total / this.pageSize))
      this.pageSize = size
    }

组件源码

<template>
  <div class="page-container">
    <span v-if="showTotal">{{total}}</span>
    <Cell :title="'上一页'" :disabled="current===1" @click.native="current > 1 && $emit('pageClick', current - 1)">&lt;</Cell><Cell v-for="(item, index) in groupList" :key="index" :active="item === current" :disabled="item==='...'" @click.native="item !== current && item !== '...' && $emit('pageClick', item)">{{item}}</Cell><Cell :title="'下一页'" :disabled="current===page" @click.native="current < page && $emit('pageClick', current + 1)">&gt;</Cell>
    <Elevator v-if="showElevator"/>
    <Options :options="pageSizeOpts" @pageSizeChange="$emit('pageSizeChange', $event)"/>
  </div>
</template>

<script>
import Cell from './Cell'
import Elevator from './Elevator'
import Options from './Options'
export default {
  name: 'Page',
  components: {Options, Elevator, Cell},
  props: {
    current: {
      type: Number,
      default: 1
    },
    total: {
      type: Number,
      default: 0
    },
    pageSize: {
      type: Number,
      default: 10
    },
    pageSizeOpts: {
      type: Array,
      default () {
        return [10, 20, 30, 40]
      }
    },
    showElevator: {
      type: Boolean,
      default: false
    },
    showTotal: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    /**
     * 生成一个从 start 到 end 的连续数组 start 和 end都需是正整数 且 start < end
     * @param start
     * @param end
     */
    generateArray (start, end) {
      return Array.from(new Array(end + 1).keys()).slice(start)
    }
  },
  computed: {
    page () {
      return Math.ceil(this.total / this.pageSize)
    },
    groupList () {
      let ret = []
      const pageTotal = this.page
      const before = 3 // current所在数的前三后四
      const after = 4
      const current = this.current
      if (pageTotal < before + after) {
        ret = this.generateArray(1, pageTotal)
      } else {
        if (current < before) {
          ret.push(...this.generateArray(1, before))
          ret.push('...')
          ret.push(pageTotal - 1)
          ret.push(pageTotal)
        } else if (current > this.page - after) {
          ret.push(1)
          ret.push(2)
          ret.push('...')
          ret.push(...this.generateArray(current - 1, current))
          ret.push(...this.generateArray(current + 1, pageTotal))
        } else {
          if (current - before > 1) {
            ret.push(1)
            if (current - before > 2) {
              ret.push('...')
            }
          }
          ret.push(...this.generateArray(current - before > 0 ? current - before : 1, current + after))
          if (current + after < pageTotal) {
            if (current + after < pageTotal - 1) {
              ret.push('...')
            }
            ret.push(pageTotal)
          }
        }
      }
      return ret
    }
  }
}
</script>

<style lang="less" scoped>
  .page-container{
    font-size: 12px;
    color: #515a6e;

    &>span{
      margin-right: 10px;
    }
  }
</style>

<template>
  <div class="page-elevator">跳至 <input title="" @blur="handleBlur" @keyup.enter="handleBlur" type="text" autocomplete="off" spellcheck="false"></div>
</template>

<script>
export default {
  name: 'Elevator',
  methods: {
    handleBlur (e) {
      let value = e.target.value
      if (value === '') {
        return
      }
      let page = value.replace(/\D/g, '')
      if (page !== '') {
        this.$emit('changeTo', page)
        e.target.value = ''
      } else {
        this.$message({
          type: 'warn',
          text: '请输入合法的页数'
        })
      }
    }
  }
}
</script>

<style lang="less" scoped>
  .page-elevator{
    display: inline-block;
    vertical-align: middle;
    height: 32px;
    line-height: 32px;
    font-size: 12px;
    color: #515a6e;
    input{
      display: inline-block;
      height: 32px;
      line-height: 1.5;
      padding: 4px 7px;
      font-size: 12px;
      border: 1px solid #dcdee2;
      color: #515a6e;
      background: #fff none;
      position: relative;
      cursor: text;
      transition: border .2s ease-in-out,background .2s ease-in-out,box-shadow .2s ease-in-out;
      border-radius: 4px;
      margin: 0 8px;
      width: 50px;

      &:focus{
        border-color: #57a3f3;
        outline: 0;
        box-shadow: 0 0 0 2px rgba(45,140,240,.2);
      }
    }
  }
</style>

<template>
  <div class="page-opt">
    <div class="selected" @click="showOpt = !showOpt">
      <span>{{options[current] + unit}}</span>
      <img class="icon" src="../../assets/icon/arrow_down.png"/>
    </div>
    <transition enter-active-class="animated bounceIn" leave-active-class="animated fadeOut">
      <ul class="opt" v-if="showOpt">
        <li :class="['opt-item', {'opt-item-active': current === index}]" v-for="(item, index) in options" @click="handleClick(index)" :key="index">{{item + unit}}</li>
      </ul>
    </transition>
  </div>
</template>

<script>
export default {
  name: 'Options',
  props: {
    options: {
      type: Array,
      default () {
        return []
      }
    }
  },
  methods: {
    handleOtherClicked (e) {
      if (!this.$el.contains(e.target)) {
        this.showOpt = false
      }
    },
    handleClick (index) {
      if (index !== this.current) {
        this.current = index
        this.showOpt = false
        this.$emit('pageSizeChange', this.options[index])
      }
    }
  },
  data () {
    return {
      current: 0,
      unit: '条/页',
      showOpt: false
    }
  },
  mounted () {
    document.addEventListener('click', this.handleOtherClicked)
  },
  destroyed () {
    document.removeEventListener('click', this.handleOtherClicked)
  }
}
</script>

<style lang="less" scoped>
  .page-opt{
    display: inline-block;
    height: 32px;
    position: relative;

    .selected{
      padding: 0 5px;
      height: 100%;
      border: 1px solid #dcdee2;
      cursor: pointer;
      border-radius: 4px;
      user-select: none;

      display: flex;
      justify-content: space-around;
      align-items: center;
      img{
        width: 12px;
        height: 100%;
        margin-left: 5px;
        object-fit: contain;
      }

      &:hover{
        border-color: #2d8cf0;
        color: #2d8cf0;
      }
    }
    .opt{
      min-width: 100%;
      list-style: none;

      max-height: 200px;
      overflow: auto;
      margin: 5px 0;
      padding: 5px 0;
      background-color: #fff;
      box-sizing: border-box;
      border-radius: 4px;
      box-shadow: 0 1px 6px rgba(0,0,0,.2);
      position: absolute;
      z-index: 900;

      .opt-item{
        margin: 0;
        line-height: normal;
        padding: 7px 16px;
        clear: both;
        color: #515a6e;
        font-size: 12px;
        white-space: nowrap;
        list-style: none;
        cursor: pointer;
        transition: background .2s ease-in-out;
        user-select: none;
        &:hover{
          background: #f3f3f3;
        }
        &-active{
          color: #2d8cf0;
          background: #f3f3f3;
        }
      }
    }
  }
</style>

<template>
  <div :class="['table-cell', {'table-cell-active': active}, {'table-cell-disabled': disabled}]" :title="localTitle">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'Cell',
  props: {
    active: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    title: {
      type: String,
      default: ''
    }
  },
  mounted () {
    this.localTitle = this.title !== '' ? this.title : this.$slots.default[0].text
  },
  data () {
    return {
      localTitle: ''
    }
  }
}
</script>

<style lang="less" scoped>
  .table-cell{
    user-select: none;
    min-width: 32px;
    height: 32px;
    list-style: none;
    text-align: center;
    cursor: pointer;
    color: #666;
    font-family: Arial,serif;
    border: 1px solid #dcdee2;
    border-radius: 4px;
    transition: all .2s ease-in-out;
    margin: 0 2px;

    display: inline-flex;
    justify-content: center;
    align-items: center;

    &:hover{
      border-color: #2d8cf0;
      color: #2d8cf0;
    }
    &-active{
      border-color: #2d8cf0;
      color: #2d8cf0;
    }
    &-disabled{
      cursor: not-allowed;
      color: #666 !important;
    }
    &-disabled:hover{
      border-color: #dcdee2;
    }
  }
</style>

猜你喜欢

转载自blog.csdn.net/csu_passer/article/details/89183314