123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- <template>
- <view
- class="u-pagination"
- :class="[disabled && 'u-pagination--disabled', simple && 'u-pagination--simple']"
- :style="[$u.addStyle(customStyle)]"
- >
- <!-- 上一页按钮 -->
- <view
- v-if="showPrevButton"
- class="u-pagination__prev"
- :class="[{ 'u-pagination__item--disabled': isFirstPage || disabled }]"
- :style="[itemStyle()]"
- @click="prevPage"
- >
- <slot name="prev" :disabled="isFirstPage || disabled">
- <text :style="[textStyle]">{{ prevText }}</text>
- </slot>
- </view>
- <!-- 简单模式 -->
- <text v-if="simple" class="u-pagination__simple" :style="[textStyle]">
- {{ innerValue }} / {{ totalPages }}
- </text>
- <!-- 完整模式页码列表 -->
- <template v-if="!simple">
- <view
- v-for="(page, index) in pageItems"
- :key="index"
- class="u-pagination__item"
- :class="[{
- 'u-pagination__item--active': page.isActive,
- 'u-pagination__item--disabled': disabled,
- 'u-pagination__item--ellipsis': page.type == 'ellipsis'
- }]"
- :style="[page.isActive ? activeItemStyle(true) : itemStyle(true)]"
- @click="handlePageClick(page)"
- >
- <slot name="page" :label="page.label" :active="page.isActive">
- <text :style="[page.isActive ? activeTextStyle : textStyle]">{{ page.label }}</text>
- </slot>
- </view>
- </template>
- <!-- 下一页按钮 -->
- <view
- v-if="showNextButton"
- class="u-pagination__next"
- :class="{ 'u-pagination__item--disabled': isLastPage || disabled }"
- :style="[itemStyle()]"
- @click="nextPage"
- >
- <slot name="next" :disabled="isLastPage || disabled">
- <text :style="textStyle">{{ nextText }}</text>
- </slot>
- </view>
- </view>
- </template>
- <script>
- import props from './props.js';
- import mixin from '../../libs/mixin/mixin';
- import mpMixin from '../../libs/mixin/mpMixin';
- /**
- * Pagination 分页
- * @description 分页器用于分隔长列表,每次只加载一个页面
- * @tutorial https://uview.d3u.cn/components/pagination.html
- * @property {Number} modelValue 当前页码(默认 1 )
- * @property {Number} total 总记录数(默认 0 )
- * @property {Number} pageSize 每页显示的记录数(默认 10 )
- * @property {Number} pagerCount 显示的页码按钮数量(默认 5 )
- * @property {Boolean} disabled 是否禁用分页(默认 false )
- * @property {Boolean} forceEllipses 是否显示省略号(默认 false )
- * @property {Boolean} simple 是否为简单分页(默认 false )
- * @property {Boolean} showPrevButton 是否展示上一页按钮(默认 true )
- * @property {Boolean} showNextButton 是否展示下一页按钮(默认 true )
- * @property {String} prevText 上一页按钮文字(默认 '上一页' )
- * @property {String} nextText 下一页按钮文字(默认 '下一页' )
- * @property {String} bgColor 背景色(默认 '#f7f7f7' )
- * @property {String} color 文本色(默认 '#606266' )
- * @property {String} activeBgColor 激活背景色(默认 '#2979ff' )
- * @property {String} activeColor 激活文本色(默认 '#ffffff' )
- * @property {String} fontSize 字体尺寸(默认 '14px' )
- * @property {String} round 圆角(默认 '4px' )
- * @property {String} borderColor 描边色(默认 '#e4e7ed' )
- * @property {String} itemWidth 每项宽度(默认 '40px' )
- * @property {String} itemHeight 每项高度(默认 '40px' )
- * @property {Object} customStyle 自定义样式
- * @event {Function} change 切换分页触发
- * @example <u-pagination v-model="currentPage" :total="100" :page-size="10"></u-pagination>
- */
- export default {
- name: "u-pagination",
- mixins: [mpMixin, mixin, props],
-
- data() {
- return {
- innerValue: 0
- }
- },
- computed: {
- // 总页数
- totalPages() {
- return Math.max(1, Math.ceil(this.total / this.pageSize));
- },
- // 是否为第一页
- isFirstPage() {
- return this.innerValue <= 1;
- },
- // 是否为最后一页
- isLastPage() {
- return this.innerValue >= this.totalPages;
- },
- // 页码项目列表
- pageItems() {
- const pageList = [];
- const totalPageCount = this.totalPages;
- const visiblePageCount = this.pagerCount;
- const currentPageNum = this.innerValue;
-
- let firstPage = 1;
- let lastPage = totalPageCount;
- const shouldLimitPages = visiblePageCount < totalPageCount;
-
- if (shouldLimitPages) {
- const halfVisible = Math.floor(visiblePageCount / 2);
- firstPage = Math.max(currentPageNum - halfVisible, 1);
- lastPage = firstPage + visiblePageCount - 1;
- if (lastPage > totalPageCount) {
- lastPage = totalPageCount;
- firstPage = lastPage - visiblePageCount + 1;
- }
- }
- // 生成基础页码列表
- for (let pageNum = firstPage; pageNum <= lastPage; pageNum++) {
- pageList.push({
- pageNumber: pageNum,
- label: `${pageNum}`,
- isActive: pageNum === currentPageNum,
- type: 'page'
- });
- }
- // 处理省略号显示逻辑
- if (shouldLimitPages && visiblePageCount > 0 && this.forceEllipses) {
- // 添加左侧省略号和首页
- if (firstPage > 1) {
- pageList.shift();
-
- // 添加左侧省略号
- pageList.unshift({
- pageNumber: firstPage - 1,
- label: '...',
- isActive: false,
- type: 'ellipsis'
- });
- // 添加首页
- pageList.unshift({
- pageNumber: 1,
- label: '1',
- isActive: currentPageNum === 1,
- type: 'page'
- });
- }
- // 添加右侧省略号和末页
- if (lastPage < totalPageCount) {
- pageList.pop();
-
- // 添加右侧省略号
- pageList.push({
- pageNumber: lastPage + 1,
- label: '...',
- isActive: false,
- type: 'ellipsis'
- });
-
- // 添加末页
- pageList.push({
- pageNumber: totalPageCount,
- label: `${totalPageCount}`,
- isActive: currentPageNum === totalPageCount,
- type: 'page'
- });
- }
- }
- return pageList;
- },
- // 项目样式
- itemStyle(isItem) {
- return (isItem) => {
- let style = {
- height: this.itemHeight,
- borderRadius: this.round
- }
- if(this.bgColor){
- style.backgroundColor = this.bgColor
- }
- if(this.borderColor){
- style.border = '1px solid ' + this.borderColor
- }
- if(isItem){
- style.width = this.itemWidth
- }
- return style;
- }
- },
- // 激活项目样式
- activeItemStyle(isItem) {
- return (isItem) => {
- let style = {
- height: this.itemHeight,
- borderRadius: this.round
- }
- if(this.activeBgColor){
- style.backgroundColor = this.activeBgColor
- }
- if(this.activeColor){
- style.color = this.activeColor
- }
- if(isItem){
- style.width = this.itemWidth
- }
- return style;
- }
- },
- // 文本样式
- textStyle() {
- return {
- color: this.color,
- fontSize: this.fontSize
- };
- },
- // 激活文本样式
- activeTextStyle() {
- return {
- color: this.activeColor,
- fontSize: this.fontSize
- };
- }
- },
- watch: {
- // #ifdef VUE3
- modelValue: {
- immediate: true,
- handler(val) {
- // 只在初始化或外部主动改变时更新内部状态
- if (val && val !== this.innerValue) {
- this.innerValue = val;
- }
- }
- },
- // #endif
- //#ifdef VUE2
- value: {
- immediate: true,
- handler(val) {
- // 只在没有modelValue且value有效时更新
- if (val && !this.innerValue) {
- this.innerValue = val;
- }
- }
- }
- // #endif
- },
- // #ifdef VUE3
- emits: ["update:modelValue", "change"],
- // #endif
- methods: {
- // 更新页码
- updatePage(page) {
- if (this.disabled) return;
- const newPage = Math.max(1, Math.min(page, this.totalPages));
- if (newPage !== this.innerValue) {
- this.innerValue = newPage;
- // #ifdef VUE2
- this.$emit('input', newPage);
- // #endif
-
- // #ifdef VUE3
- this.$emit('update:modelValue', newPage);
- // #endif
- this.$emit('change', newPage);
- }
- },
- // 上一页
- prevPage() {
- if (this.isFirstPage || this.disabled) return;
- this.updatePage(this.innerValue - 1);
- },
- // 下一页
- nextPage() {
- if (this.isLastPage || this.disabled) return;
- this.updatePage(this.innerValue + 1);
- },
- // 处理页码点击
- handlePageClick(page) {
- if (this.disabled || page.isActive) return;
- this.updatePage(page.pageNumber);
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- @import "../../libs/css/components.scss";
- .u-pagination {
- @include flex;
- align-items: center;
- justify-content: center;
-
- &--simple {
- .u-pagination__simple {
- margin: 0 12px;
- }
- }
-
- &--disabled {
- opacity: 0.8;
- // #ifdef H5
- pointer-events: none;
- // #endif
- }
-
- &__item,
- &__prev,
- &__next {
- @include flex;
- align-items: center;
- justify-content: center;
- // #ifndef APP-NVUE
- user-select: none;
- // #endif
- // #ifdef H5
- cursor: pointer;
- // #endif
-
- &--active {
- border-color: transparent;
- }
-
- &--disabled {
- opacity: 0.5;
- // #ifdef H5
- cursor: not-allowed;
- // #endif
- }
-
- &--ellipsis {
-
- // #ifdef H5
- cursor: pointer;
- // #endif
- }
- }
-
- &__prev {
- margin-right: 3px;
- }
- &__next {
- margin-left: 3px;
- }
- &__prev,
- &__next {
- padding: 0 10px;
- &:active {
- opacity: 0.8;
- }
- }
-
- &__item {
- margin: 0 3px;
- }
- }
- </style>
|