个人组件库文档地址
分页器效果图

分页组件代码
<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>
<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: "",
},
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);
},
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);
},
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);
},
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);
}
},
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;
},
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,
default: "text",
},
placeholder: {
type: String,
default: "",
},
value: {
type: String | Number,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
clearable: {
type: Boolean,
default: false,
},
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: {
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", {
[this.vpFormItem.prop]: {
ruleMessage: this.ruleMessage,
},
});
} else {
this.ruleMessage = "";
this.$bus.$emit("ruleChange", {
[this.vpFormItem.prop]: {
ruleMessage: this.ruleMessage,
},
});
}
}
});
}
});
},
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", {
[this.vpFormItem.prop]: {
ruleMessage: this.ruleMessage,
},
});
} else {
if (this.ruleMessage !== "") {
this.ruleMessage = "";
this.$bus.$emit("ruleChange", {
[this.vpFormItem.prop]: {
ruleMessage: this.ruleMessage,
},
});
}
}
}
});
}
});
},
focusHandle(e) {
this.hasFocus = true;
this.$emit("focus", e);
},
showPWDHandle() {
this.isShowPWD = !this.isShowPWD;
},
clearHandle() {
this.$emit("input", "");
},
textareaInputHandle(e) {
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 | 只有一页时是否隐藏 | false | true/false |
| prev-text | 替代图标显示的上一页文字 | —— | —— |
| next-text | 替代图标显示的下一页文字 | —— | —— |
Events
| 事件名称 | 说明 | 回调参数 |
|---|
| current-change | currentPage 改变时会触发 | 当前页 |
| prev-click | 用户点击上一页按钮改变当前页后触发 | 当前页 |
| next-click | 用户点击下一页按钮改变当前页后触发 | 当前页 |