502 lines
12 KiB
Vue
Raw Permalink Normal View History

2025-03-11 17:57:17 +08:00
<template>
<view class="uni-pagination">
<!-- #ifndef MP -->
<picker
v-if="showPageSize === true || showPageSize === 'true'"
class="select-picker"
mode="selector"
:value="pageSizeIndex"
:range="pageSizeRange"
@change="pickerChange"
@cancel="pickerClick"
@click.native="pickerClick">
<button type="default" size="mini" :plain="true">
<text>{{ pageSizeRange[pageSizeIndex] }} {{ piecePerPage }}</text>
<uni-icons
class="select-picker-icon"
type="arrowdown"
size="12"
color="#999"></uni-icons>
</button>
</picker>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view class="uni-pagination__total is-phone-hide"> {{ total }} </view>
<!-- #endif -->
<view
class="uni-pagination__btn"
:class="
currentIndex === 1
? 'uni-pagination--disabled'
: 'uni-pagination--enabled'
"
:hover-class="currentIndex === 1 ? '' : 'uni-pagination--hover'"
:hover-start-time="20"
:hover-stay-time="70"
@click="clickLeft">
<template v-if="showIcon === true || showIcon === 'true'">
<uni-icons color="#666" size="16" type="left" />
</template>
<template v-else>
<text class="uni-pagination__child-btn">{{ prevPageText }}</text>
</template>
</view>
<view class="uni-pagination__num uni-pagination__num-flex-none">
<view class="uni-pagination__num-current">
<text
class="uni-pagination__num-current-text is-pc-hide current-index-text">
{{ currentIndex }}
</text>
<text class="uni-pagination__num-current-text is-pc-hide">
/{{ maxPage || 0 }}
</text>
<!-- #ifndef APP-NVUE -->
<view
v-for="(item, index) in paper"
:key="index"
:class="{ 'page--active': item === currentIndex }"
class="uni-pagination__num-tag tag--active is-phone-hide"
@click.top="selectPage(item, index)">
<text>{{ item }}</text>
</view>
<!-- #endif -->
</view>
</view>
<view
class="uni-pagination__btn"
:class="
currentIndex >= maxPage
? 'uni-pagination--disabled'
: 'uni-pagination--enabled'
"
:hover-class="currentIndex === maxPage ? '' : 'uni-pagination--hover'"
:hover-start-time="20"
:hover-stay-time="70"
@click="clickRight">
<template v-if="showIcon === true || showIcon === 'true'">
<uni-icons color="#666" size="16" type="right" />
</template>
<template v-else>
<text class="uni-pagination__child-btn">{{ nextPageText }}</text>
</template>
</view>
</view>
</template>
<script>
/**
* Pagination 分页器
* @description 分页器组件用于展示页码请求数据等
* @tutorial https://ext.dcloud.net.cn/plugin?id=32
* @property {String} prevText 左侧按钮文字
* @property {String} nextText 右侧按钮文字
* @property {String} piecePerPageText /页文字
* @property {Number} current 当前页
* @property {Number} total 数据总量
* @property {Number} pageSize 每页数据量
* @property {Boolean} showIcon = [true|false] 是否以 icon 形式展示按钮
* @property {Boolean} showPageSize = [true|false] 是否展示每页条数
* @property {Array} pageSizeRange = [20, 50, 100, 500] 每页条数选框
* @event {Function} change 点击页码按钮时触发 ,e={type,current} current为当前页type值为next/prev表示点击的是上一页还是下一个
* * @event {Function} pageSizeChange 当前每页条数改变时触发 ,e={pageSize} pageSize 为当前所选的每页条数
*/
import { initVueI18n } from "@dcloudio/uni-i18n";
import messages from "./i18n/index.js";
const { t } = initVueI18n(messages);
export default {
name: "UniPagination",
emits: ["update:modelValue", "input", "change", "pageSizeChange"],
props: {
value: {
type: [Number, String],
default: 1,
},
modelValue: {
type: [Number, String],
default: 1,
},
prevText: {
type: String,
},
nextText: {
type: String,
},
piecePerPageText: {
type: String,
},
current: {
type: [Number, String],
default: 1,
},
total: {
// 数据总量
type: [Number, String],
default: 0,
},
pageSize: {
// 每页数据量
type: [Number, String],
default: 10,
},
showIcon: {
// 是否以 icon 形式展示按钮
type: [Boolean, String],
default: false,
},
showPageSize: {
// 是否以 icon 形式展示按钮
type: [Boolean, String],
default: false,
},
pagerCount: {
type: Number,
default: 7,
},
pageSizeRange: {
type: Array,
default: () => [20, 50, 100, 500],
},
},
data() {
return {
pageSizeIndex: 0,
currentIndex: 1,
paperData: [],
pickerShow: false,
};
},
computed: {
piecePerPage() {
return this.piecePerPageText || t("uni-pagination.piecePerPage");
},
prevPageText() {
return this.prevText || t("uni-pagination.prevText");
},
nextPageText() {
return this.nextText || t("uni-pagination.nextText");
},
maxPage() {
let maxPage = 1;
let total = Number(this.total);
let pageSize = Number(this.pageSize);
if (total && pageSize) {
maxPage = Math.ceil(total / pageSize);
}
return maxPage;
},
paper() {
const num = this.currentIndex;
// TODO 最大页数
const pagerCount = this.pagerCount;
// const total = 181
const total = this.total;
const pageSize = this.pageSize;
let totalArr = [];
let showPagerArr = [];
let pagerNum = Math.ceil(total / pageSize);
for (let i = 0; i < pagerNum; i++) {
totalArr.push(i + 1);
}
showPagerArr.push(1);
const totalNum = totalArr[totalArr.length - (pagerCount + 1) / 2];
totalArr.forEach((item, index) => {
if ((pagerCount + 1) / 2 >= num) {
if (item < pagerCount + 1 && item > 1) {
showPagerArr.push(item);
}
} else if (num + 2 <= totalNum) {
if (
item > num - (pagerCount + 1) / 2 &&
item < num + (pagerCount + 1) / 2
) {
showPagerArr.push(item);
}
} else {
if (
(item > num - (pagerCount + 1) / 2 ||
pagerNum - pagerCount < item) &&
item < totalArr[totalArr.length - 1]
) {
showPagerArr.push(item);
}
}
});
if (pagerNum > pagerCount) {
if ((pagerCount + 1) / 2 >= num) {
showPagerArr[showPagerArr.length - 1] = "...";
} else if (num + 2 <= totalNum) {
showPagerArr[1] = "...";
showPagerArr[showPagerArr.length - 1] = "...";
} else {
showPagerArr[1] = "...";
}
showPagerArr.push(totalArr[totalArr.length - 1]);
} else {
if ((pagerCount + 1) / 2 >= num) {
} else if (num + 2 <= totalNum) {
} else {
showPagerArr.shift();
showPagerArr.push(totalArr[totalArr.length - 1]);
}
}
return showPagerArr;
},
},
watch: {
current: {
immediate: true,
handler(val, old) {
if (val < 1) {
this.currentIndex = 1;
} else {
this.currentIndex = val;
}
},
},
value: {
immediate: true,
handler(val) {
if (Number(this.current) !== 1) return;
if (val < 1) {
this.currentIndex = 1;
} else {
this.currentIndex = val;
}
},
},
pageSizeIndex(val) {
this.$emit("pageSizeChange", this.pageSizeRange[val]);
},
},
methods: {
pickerChange(e) {
this.pageSizeIndex = e.detail.value;
this.pickerClick();
},
pickerClick() {
// #ifdef H5
const body = document.querySelector("body");
if (!body) return;
const className = "uni-pagination-picker-show";
this.pickerShow = !this.pickerShow;
if (this.pickerShow) {
body.classList.add(className);
} else {
setTimeout(() => body.classList.remove(className), 300);
}
// #endif
},
// 选择标签
selectPage(e, index) {
if (parseInt(e)) {
this.currentIndex = e;
this.change("current");
} else {
let pagerNum = Math.ceil(this.total / this.pageSize);
// let pagerNum = Math.ceil(181 / this.pageSize)
// 上一页
if (index <= 1) {
if (this.currentIndex - 5 > 1) {
this.currentIndex -= 5;
} else {
this.currentIndex = 1;
}
return;
}
// 下一页
if (index >= 6) {
if (this.currentIndex + 5 > pagerNum) {
this.currentIndex = pagerNum;
} else {
this.currentIndex += 5;
}
return;
}
}
},
clickLeft() {
if (Number(this.currentIndex) === 1) {
return;
}
this.currentIndex -= 1;
this.change("prev");
},
clickRight() {
if (Number(this.currentIndex) >= this.maxPage) {
return;
}
this.currentIndex += 1;
this.change("next");
},
change(e) {
this.$emit("input", this.currentIndex);
this.$emit("update:modelValue", this.currentIndex);
this.$emit("change", {
type: e,
current: this.currentIndex,
});
},
},
};
</script>
<style lang="scss" scoped>
$uni-primary: #2979ff !default;
.uni-pagination {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
position: relative;
overflow: hidden;
flex-direction: row;
justify-content: center;
align-items: center;
}
.uni-pagination__total {
font-size: 14px;
color: #999;
margin-right: 15px;
}
.uni-pagination__btn {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
padding: 0 8px;
line-height: 30px;
font-size: 12px;
position: relative;
background-color: #f0f0f0;
flex-direction: row;
justify-content: center;
align-items: center;
text-align: center;
border-radius: 5px;
// border-width: 1px;
// border-style: solid;
// border-color: $uni-border-color;
}
.uni-pagination__child-btn {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
font-size: 12px;
position: relative;
flex-direction: row;
justify-content: center;
align-items: center;
text-align: center;
color: #666;
font-size: 12px;
}
.uni-pagination__num {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 30px;
line-height: 30px;
font-size: 12px;
color: #666;
margin: 0 5px;
}
.uni-pagination__num-tag {
/* #ifdef H5 */
cursor: pointer;
min-width: 30px;
/* #endif */
margin: 0 5px;
height: 30px;
text-align: center;
line-height: 30px;
// border: 1px red solid;
color: #999;
border-radius: 4px;
// border-width: 1px;
// border-style: solid;
// border-color: $uni-border-color;
}
.uni-pagination__num-current {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-pagination__num-current-text {
font-size: 15px;
}
.current-index-text {
color: $uni-primary;
}
.uni-pagination--enabled {
color: #333333;
opacity: 1;
}
.uni-pagination--disabled {
opacity: 0.5;
/* #ifdef H5 */
cursor: default;
/* #endif */
}
.uni-pagination--hover {
color: rgba(0, 0, 0, 0.6);
background-color: #eee;
}
.tag--active:hover {
color: $uni-primary;
}
.page--active {
color: #fff;
background-color: $uni-primary;
}
.page--active:hover {
color: #fff;
}
/* #ifndef APP-NVUE */
.is-pc-hide {
display: block;
}
.is-phone-hide {
display: none;
}
// @media screen and (min-width: 450px) {
// .is-pc-hide {
// display: none;
// }
// .is-phone-hide {
// display: block;
// }
// .uni-pagination__num-flex-none {
// flex: none;
// }
// }
/* #endif */
</style>