Vue 个人组件库 —— 分页器

224 阅读1分钟

个人组件库文档地址

分页器效果图

1633923005(1).png

分页组件代码

<template>
  <div class="vp-pagination" v-if="isHidden">
    <!-- 前 -->
    <div
      :class="['vp-pagination_button', !prevText ? 'vp-pagination_pre' : '']"
      @click="handleClickPre"
    >
      <template v-if="!prevText">
        <span class="iconfont icon-xiangxia"></span>
      </template>
      <template v-else>
        <span>{{ prevText }}</span>
      </template>
    </div>

    <!-- 内容 -->
    <template v-if="lastPageNum <= 7">
      <div
        :class="[
          'vp-pagination_button',
          page === currentPage ? 'vp-pagination_button_active' : '',
        ]"
        v-for="page in lastPageNum"
        :key="page"
        @click="handleClickPageNum(page)"
      >
        {{ page }}
      </div>
    </template>
    <template v-else>
      <template>
        <!-- 第一个 -->
        <div
          :class="[
            'vp-pagination_button',
            1 === currentPage ? 'vp-pagination_button_active' : '',
          ]"
          @click="handleClickPageNum(1)"
        >
          1
        </div>
        <div
          v-if="hasLeft"
          class="vp-pagination_button"
          @click="handleClickPreBtn"
        >
          ...
        </div>
        <!-- 中间 -->
        <div
          :class="[
            'vp-pagination_button',
            page === currentPage ? 'vp-pagination_button_active' : '',
          ]"
          v-for="page in middlePageNumArr"
          :key="page"
          @click="handleClickPageNum(page)"
        >
          {{ page }}
        </div>
        <div
          v-if="hasRight"
          class="vp-pagination_button"
          @click="handleClickNextBtn"
        >
          ...
        </div>
        <!-- 最后一个 -->
        <div
          :class="[
            'vp-pagination_button',
            lastPageNum === currentPage ? 'vp-pagination_button_active' : '',
          ]"
          @click="handleClickPageNum(lastPageNum)"
        >
          {{ lastPageNum }}
        </div>
      </template>
    </template>

    <!-- 后 -->
    <div
      :class="['vp-pagination_button', !nextText ? 'vp-pagination_next' : '']"
      @click="handleClickNext"
    >
      <template v-if="!nextText">
        <span class="iconfont icon-xiangxia"></span>
      </template>
      <template v-else>
        <span>{{ nextText }}</span>
      </template>
    </div>

    <!-- jumper -->
    <template v-if="hasJumper">
      <div class="vp-pagination_jumper_container">
        <span class="vp-pagination_jumper_txt"> 前往 </span>
        <div class="vp-pagination_input">
          <vp-input
            v-model="page"
            @input="handleInput"
            @blur="handleInputBlur"
          />
        </div>
        <span class="vp-pagination_jumper_txt"></span>
      </div>
    </template>
  </div>
</template>

<script>
import VpInput from "./input/vp-input.vue";
export default {
  name: "vpPagination",
  props: {
    // 当前页码
    currentPage: {
      type: Number,
      default: 1,
    },
    // 每页条大小
    pageSize: {
      type: Number,
      default: 10,
    },
    // 总数
    total: {
      type: Number,
      default: 10,
    },
    // 单页是否被隐藏
    hideOnSinglePage: {
      type: Boolean,
      default: false,
    },
    // 布局
    layout: {
      type: String,
      default: "", // jumper
    },
    // 上一页文字
    prevText: {
      type: String,
      default: "",
    },
    // 下一页文字
    nextText: {
      type: String,
      default: "",
    },
  },
  components: {
    VpInput,
  },
  data() {
    return {
      hasLeft: false,
      hasRight: false,
      page: this.currentPage,
      filterPage: 1,
    };
  },
  watch: {
    currentPage(newVal) {
      this.$emit("current-change", newVal);
      this.page = newVal;
    },
  },
  computed: {
    // 最后一页的页码数
    lastPageNum() {
      return Math.ceil(this.total / this.pageSize);
    },
    // 中间的5个页码数组
    middlePageNumArr() {
      let result = [];
      switch (this.currentPage) {
        case 1:
          result = [2, 3, 4, 5, 6];
          break;
        case this.lastPageNum:
          result = [
            this.lastPageNum - 5,
            this.lastPageNum - 4,
            this.lastPageNum - 3,
            this.lastPageNum - 2,
            this.lastPageNum - 1,
          ];
          break;
        default:
          if (this.currentPage - 3 > 1) {
            this.hasLeft = true;
            result = [
              this.currentPage - 2,
              this.currentPage - 1,
              this.currentPage,
              this.currentPage + 1,
              this.currentPage + 2,
            ];
          } else {
            this.hasLeft = false;
            result = [2, 3, 4, 5, 6];
          }
          if (this.currentPage + 3 < this.lastPageNum) {
            this.hasRight = true;
            result = result || [
              this.currentPage - 2,
              this.currentPage - 1,
              this.currentPage,
              this.currentPage + 1,
              this.currentPage + 2,
            ];
          } else {
            this.hasRight = false;
            result = [
              this.lastPageNum - 5,
              this.lastPageNum - 4,
              this.lastPageNum - 3,
              this.lastPageNum - 2,
              this.lastPageNum - 1,
            ];
          }
          break;
      }
      return result;
    },

    // 分页器隐藏
    isHidden() {
      return !(this.lastPageNum === 1 && this.hideOnSinglePage);
    },
    // 是否 jumper
    hasJumper() {
      return /jumper/.test(this.layout);
    },
  },
  created() {
    this.handleInit();
  },
  mounted() {},
  methods: {
    // 点击页码
    handleClickPageNum(pageNum) {
      this.$emit("update:currentPage", pageNum);
    },
    // 点击前一页
    handleClickPre() {
      if (this.currentPage !== 1) {
        this.$emit("update:currentPage", this.currentPage - 1);
      }
      let currentPage = this.currentPage === 1 && 1;
      currentPage = this.currentPage !== 1 && this.currentPage - 1;
      currentPage = currentPage || 1;
      this.$emit("pre-click", currentPage);
    },

    // 点击下一页
    handleClickNext() {
      if (this.currentPage !== this.lastPageNum) {
        this.$emit("update:currentPage", this.currentPage + 1);
      }
      let currentPage =
        this.currentPage === this.lastPageNum && this.lastPageNum;
      currentPage =
        this.currentPage !== this.lastPageNum && this.currentPage + 1;
      currentPage = currentPage || this.lastPageNum;
      this.$emit("next-click", currentPage);
    },

    // 初始化是否显示 left right
    handleInit() {
      switch (this.currentPage) {
        case 1:
          if (this.lastPageNum > 7) {
            this.hasRight = true;
          }
          break;
        case this.lastPageNum:
          if (this.lastPageNum > 7) {
            this.hasLeft = true;
          }
          break;
        default:
          if (this.currentPage - 3 > 1) {
            this.hasLeft = true;
          } else {
            this.hasLeft = false;
          }
          if (this.currentPage + 3 < this.lastPageNum) {
            this.hasRight = true;
          } else {
            this.hasRight = false;
          }
          break;
      }
    },

    /**
     * 点击左边 ... 按钮
     */
    handleClickPreBtn() {
      if (this.currentPage !== 1) {
        this.$emit("update:currentPage", this.currentPage - 1);
      }
    },

    /**
     * 点击右边 ... 按钮
     */
    handleClickNextBtn() {
      if (this.currentPage !== this.lastPageNum) {
        this.$emit("update:currentPage", this.currentPage + 1);
      }
    },

    /**
     * 输入框 - input
     */
    handleInput(val) {
      let newVal = String(val);
      newVal = newVal.replace(/\s/g, "");
      newVal = newVal.replace(/[a-zA-Z]/g, "");
      newVal = newVal.replace(/^0/, "");
      newVal = Number(newVal);
      this.page = newVal;
      val = newVal;
    },

    /**
     * input blur 事件
     */
    handleInputBlur() {
      let page = this.lastPageNum >= this.page ? this.page : this.currentPage;
      this.$emit("update:currentPage", page);
    },
  },
};
</script>

<style lang="less" scoped>
.vp-pagination {
  .vp-pagination_pre {
    display: inline-block;
    transform: rotate(90deg);
  }

  .vp-pagination_next {
    display: inline-block;
    transform: rotate(-90deg);
  }

  .icon-xiangxia {
    font-size: 14px;
  }

  .vp-pagination_button {
    min-width: 20px;
    min-height: 20px;
    text-align: center;
    line-height: 20px;
    padding: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    cursor: pointer;
    margin-right: 10px;
    font-weight: 700;
    display: inline-block;

    &:last-child {
      margin-right: 0;
    }

    &:hover {
      color: #409eff;
    }
  }

  .vp-pagination_button_active {
    color: #409eff;
  }

  .vp-pagination_jumper_container {
    display: inline-block;
    margin-left: 10px;

    .vp-pagination_jumper_txt {
      color: #606266;
    }

    .vp-pagination_input {
      width: 45px;
      display: inline-block;

      .vp-input {
        /deep/.vp-input-inner {
          height: 30px;
        }
        /deep/input {
          text-align: center;
        }
      }
    }
  }
}
</style>

输入框组件代码

<template>
  <div class="vp-input">
    <input
      v-if="type !== 'textarea'"
      :class="[
        'vp-input-inner',
        hasFocus ? 'vp-input-inner_focus' : '',
        disabled ? 'input-disabled' : '',
      ]"
      :type="isShowPWD ? 'text' : type"
      :placeholder="placeholder"
      :value="value"
      @input="inputHandle"
      :disabled="disabled"
      @blur="blurHandle"
      @focus="focusHandle"
    />
    <!--  -->
    <textarea
      v-else
      class="vp-input-textarea"
      name=""
      id=""
      :cols="cols"
      :rows="rows"
      :value="value"
      @input="textareaInputHandle"
      :readonly="readonly"
      :maxlength="maxlength"
      @blur="blurHandle"
      @focus="focusHandle"
    ></textarea>
    <span
      v-if="type === 'password' && value"
      class="iconfont"
      :class="isShowPWD ? 'icon-eye' : 'icon-eye1'"
      @click="showPWDHandle"
    ></span>
    <span
      v-if="clearable && value"
      class="iconfont icon-clear_circle_outlined"
      @click="clearHandle"
    ></span>
  </div>
</template>

<script>
export default {
  name: "vpInput",
  props: {
    type: {
      type: String, // "text" | "password" | "textarea"
      default: "text",
    },
    placeholder: {
      type: String,
      default: "",
    },
    value: {
      type: String | Number,
      default: "",
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    clearable: {
      type: Boolean,
      default: false,
    },
    // textarea 特有
    cols: {
      type: Number,
      default: 20,
    },
    rows: {
      type: Number,
      default: 5,
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    maxlength: {
      type: Number,
      default: 100,
    },
  },
  inject: {
    vpFormItem: {
      default: {},
    },
    vpForm: {
      default: {},
    },
  },
  watch: {
    rule(newRule) {
      newRule.forEach((item) => {
        let trigger = item.trigger;
        if (
          trigger &&
          Object.prototype.toString.call(trigger) === "[object String]"
        ) {
          if (trigger === "input") {
            this.inputRule.push(item);
          } else if (trigger === "blur") {
            this.blurRule.push(item);
          }
        } else if (
          trigger &&
          Object.prototype.toString.call(trigger) === "[object Array]"
        ) {
          trigger.forEach((it) => {
            if (it === "input") {
              this.inputRule.push(item);
            } else if (it === "blur") {
              this.blurRule.push(item);
            }
          });
        }
      });
    },
  },
  data() {
    return {
      isShowPWD: false,
      rule: [],
      inputRule: [],
      blurRule: [],
      ruleMessage: "",
      hasFocus: false,
    };
  },
  watch: {
    value(newVal) {
      this.$emit("input", newVal);
    },
  },
  created() {
    if (this.vpForm.rules && this.vpFormItem.prop) {
      this.rule = this.vpForm.rules[this.vpFormItem.prop];
    }
  },
  mounted() {},
  methods: {
    // 输入input事件
    inputHandle(e) {
      this.$emit("input", e.target.value, e);
      this.$nextTick(() => {
        let inputRule = this.inputRule;
        if (inputRule) {
          inputRule.forEach((rule) => {
            if (rule.required) {
              if (this.value === "") {
                this.ruleMessage = rule.message;
                this.$bus.$emit("ruleChange", {
                  // ruleMessage: this.ruleMessage,
                  [this.vpFormItem.prop]: {
                    ruleMessage: this.ruleMessage,
                  },
                });
              } else {
                this.ruleMessage = "";
                this.$bus.$emit("ruleChange", {
                  // ruleMessage: this.ruleMessage,
                  [this.vpFormItem.prop]: {
                    ruleMessage: this.ruleMessage,
                  },
                });
              }
            }
          });
        }
      });
    },
    // input blur 事件
    blurHandle(e) {
      this.hasFocus = false;
      this.$emit("blur", e);
      this.$nextTick(() => {
        let blurRule = this.blurRule;
        if (blurRule) {
          blurRule.forEach((rule) => {
            if (rule.required) {
              if (this.value === "") {
                this.ruleMessage = rule.message;
                this.$bus.$emit("ruleChange", {
                  // ruleMessage: this.ruleMessage,
                  [this.vpFormItem.prop]: {
                    ruleMessage: this.ruleMessage,
                  },
                });
              } else {
                if (this.ruleMessage !== "") {
                  this.ruleMessage = "";
                  this.$bus.$emit("ruleChange", {
                    // ruleMessage: this.ruleMessage,
                    [this.vpFormItem.prop]: {
                      ruleMessage: this.ruleMessage,
                    },
                  });
                }
              }
            }
          });
        }
      });
    },
    // focus 事件
    focusHandle(e) {
      this.hasFocus = true;
      this.$emit("focus", e);
    },
    // 切换密码显示
    showPWDHandle() {
      this.isShowPWD = !this.isShowPWD;
    },
    // 清除事件
    clearHandle() {
      this.$emit("input", "");
    },
    // textarea input 事件
    textareaInputHandle(e) {
      // console.log(e.target.value);
      this.$emit("input", e.target.value);
    },
  },
};
</script>
<style lang="less" scoped>
// 默认样式
.vp-input {
  width: 100%;
  display: inline-block;
  position: relative;

  span {
    cursor: pointer;
    position: absolute;
    top: 9px;
    right: 5px;
  }
  .vp-input-inner {
    box-sizing: border-box;
    width: 100%;
    height: 35px;
    // border-color: rgb(94, 170, 214);
    // border: 1px solid rgb(94, 170, 214);
    border: 1px solid #dcdfe6;
    outline: none;
    color: rgb(148, 146, 144);
    border-radius: 5px;
    border-width: 1px;
    padding: 5px 10px;
    transition: border 0.2s;

    &:hover {
      border: 1px solid #c0c4cc;
    }
  }

  .vp-input-textarea {
    outline: none;
    // border-color: rgb(94, 170, 214);
    border: 1px solid #dcdfe6;

    &:hover {
      border: 1px solid #c0c4cc;
    }
  }
}

.input-disabled {
  cursor: not-allowed;
  background-color: #f5f7fa;
  border-color: #e4e7ed;
  color: #c0c4cc;
}

.vp-input-inner_focus {
  border: 1px solid #409eff !important;
}
</style>

Pagination Attributes

参数说明默认值可选值
total总条目数————
current-page当前页数,支持 .sync 修饰符1——
layout组件布局——'jumper'
hide-on-single-page只有一页时是否隐藏falsetrue/false
prev-text替代图标显示的上一页文字————
next-text替代图标显示的下一页文字————

Events

事件名称说明回调参数
current-changecurrentPage 改变时会触发当前页
prev-click用户点击上一页按钮改变当前页后触发当前页
next-click用户点击下一页按钮改变当前页后触发当前页