import PropTypes from '../_util/vue-types'; import BaseMixin from '../_util/BaseMixin'; import { hasProp, getComponent, splitAttrs, isValidElement } from '../_util/props-util'; import Pager from './Pager'; import Options from './Options'; import LOCALE from './locale/zh_CN'; import KEYCODE from './KeyCode'; import classNames from '../_util/classNames'; import { defineComponent, withDirectives } from 'vue'; import antInput from '../_util/antInputDirective'; import { cloneElement } from '../_util/vnode'; import firstNotUndefined from '../_util/firstNotUndefined'; // 是否是正整数 function isInteger(value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; } function defaultItemRender({ originalElement }) { return originalElement; } function calculatePage(p, state, props) { const pageSize = typeof p === 'undefined' ? state.statePageSize : p; return Math.floor((props.total - 1) / pageSize) + 1; } export default defineComponent({ compatConfig: { MODE: 3 }, name: 'Pagination', mixins: [BaseMixin], inheritAttrs: false, props: { disabled: { type: Boolean, default: undefined }, prefixCls: PropTypes.string.def('rc-pagination'), selectPrefixCls: PropTypes.string.def('rc-select'), current: Number, defaultCurrent: PropTypes.number.def(1), total: PropTypes.number.def(0), pageSize: Number, defaultPageSize: PropTypes.number.def(10), hideOnSinglePage: { type: Boolean, default: false }, showSizeChanger: { type: Boolean, default: undefined }, showLessItems: { type: Boolean, default: false }, // showSizeChange: PropTypes.func.def(noop), selectComponentClass: PropTypes.any, showPrevNextJumpers: { type: Boolean, default: true }, showQuickJumper: PropTypes.oneOfType([PropTypes.looseBool, PropTypes.object]).def(false), showTitle: { type: Boolean, default: true }, pageSizeOptions: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), buildOptionText: Function, showTotal: Function, simple: { type: Boolean, default: undefined }, locale: PropTypes.object.def(LOCALE), itemRender: PropTypes.func.def(defaultItemRender), prevIcon: PropTypes.any, nextIcon: PropTypes.any, jumpPrevIcon: PropTypes.any, jumpNextIcon: PropTypes.any, totalBoundaryShowSizeChanger: PropTypes.number.def(50), }, data() { const props = this.$props; let current = firstNotUndefined([this.current, this.defaultCurrent]); const pageSize = firstNotUndefined([this.pageSize, this.defaultPageSize]); current = Math.min(current, calculatePage(pageSize, undefined, props)); return { stateCurrent: current, stateCurrentInputValue: current, statePageSize: pageSize, }; }, watch: { current(val) { this.setState({ stateCurrent: val, stateCurrentInputValue: val, }); }, pageSize(val) { const newState: any = {}; let current = this.stateCurrent; const newCurrent = calculatePage(val, this.$data, this.$props); current = current > newCurrent ? newCurrent : current; if (!hasProp(this, 'current')) { newState.stateCurrent = current; newState.stateCurrentInputValue = current; } newState.statePageSize = val; this.setState(newState); }, stateCurrent(_val, oldValue) { // When current page change, fix focused style of prev item // A hacky solution of https://github.com/ant-design/ant-design/issues/8948 this.$nextTick(() => { if (this.$refs.paginationNode) { const lastCurrentNode = this.$refs.paginationNode.querySelector( `.${this.prefixCls}-item-${oldValue}`, ); if (lastCurrentNode && document.activeElement === lastCurrentNode) { lastCurrentNode.blur(); } } }); }, total() { const newState: any = {}; const newCurrent = calculatePage(this.pageSize, this.$data, this.$props); if (hasProp(this, 'current')) { const current = Math.min(this.current, newCurrent); newState.stateCurrent = current; newState.stateCurrentInputValue = current; } else { let current = this.stateCurrent; if (current === 0 && newCurrent > 0) { current = 1; } else { current = Math.min(this.stateCurrent, newCurrent); } newState.stateCurrent = current; } this.setState(newState); }, }, methods: { getJumpPrevPage() { return Math.max(1, this.stateCurrent - (this.showLessItems ? 3 : 5)); }, getJumpNextPage() { return Math.min( calculatePage(undefined, this.$data, this.$props), this.stateCurrent + (this.showLessItems ? 3 : 5), ); }, getItemIcon(icon, label) { const { prefixCls } = this.$props; const iconNode = getComponent(this, icon, this.$props) || ( ); return iconNode; }, getValidValue(e) { const inputValue = e.target.value; const allPages = calculatePage(undefined, this.$data, this.$props); const { stateCurrentInputValue } = this.$data; let value; if (inputValue === '') { value = inputValue; } else if (isNaN(Number(inputValue))) { value = stateCurrentInputValue; } else if (inputValue >= allPages) { value = allPages; } else { value = Number(inputValue); } return value; }, isValid(page) { return isInteger(page) && page !== this.stateCurrent; }, shouldDisplayQuickJumper() { const { showQuickJumper, pageSize, total } = this.$props; if (total <= pageSize) { return false; } return showQuickJumper; }, // calculatePage (p) { // let pageSize = p // if (typeof pageSize === 'undefined') { // pageSize = this.statePageSize // } // return Math.floor((this.total - 1) / pageSize) + 1 // }, handleKeyDown(event) { if (event.keyCode === KEYCODE.ARROW_UP || event.keyCode === KEYCODE.ARROW_DOWN) { event.preventDefault(); } }, handleKeyUp(e) { if (e.isComposing || e.target.composing) return; const value = this.getValidValue(e); const stateCurrentInputValue = this.stateCurrentInputValue; if (value !== stateCurrentInputValue) { this.setState({ stateCurrentInputValue: value, }); } if (e.keyCode === KEYCODE.ENTER) { this.handleChange(value); } else if (e.keyCode === KEYCODE.ARROW_UP) { this.handleChange(value - 1); } else if (e.keyCode === KEYCODE.ARROW_DOWN) { this.handleChange(value + 1); } }, changePageSize(size) { let current = this.stateCurrent; const preCurrent = current; const newCurrent = calculatePage(size, this.$data, this.$props); current = current > newCurrent ? newCurrent : current; // fix the issue: // Once 'total' is 0, 'current' in 'onShowSizeChange' is 0, which is not correct. if (newCurrent === 0) { current = this.stateCurrent; } if (typeof size === 'number') { if (!hasProp(this, 'pageSize')) { this.setState({ statePageSize: size, }); } if (!hasProp(this, 'current')) { this.setState({ stateCurrent: current, stateCurrentInputValue: current, }); } } this.__emit('update:pageSize', size); if (current !== preCurrent) { this.__emit('update:current', current); } this.__emit('showSizeChange', current, size); this.__emit('change', current, size); }, handleChange(p) { const { disabled } = this.$props; let page = p; if (this.isValid(page) && !disabled) { const currentPage = calculatePage(undefined, this.$data, this.$props); if (page > currentPage) { page = currentPage; } else if (page < 1) { page = 1; } if (!hasProp(this, 'current')) { this.setState({ stateCurrent: page, stateCurrentInputValue: page, }); } // this.__emit('input', page) this.__emit('update:current', page); this.__emit('change', page, this.statePageSize); return page; } return this.stateCurrent; }, prev() { if (this.hasPrev()) { this.handleChange(this.stateCurrent - 1); } }, next() { if (this.hasNext()) { this.handleChange(this.stateCurrent + 1); } }, jumpPrev() { this.handleChange(this.getJumpPrevPage()); }, jumpNext() { this.handleChange(this.getJumpNextPage()); }, hasPrev() { return this.stateCurrent > 1; }, hasNext() { return this.stateCurrent < calculatePage(undefined, this.$data, this.$props); }, getShowSizeChanger() { const { showSizeChanger, total, totalBoundaryShowSizeChanger } = this.$props; if (typeof showSizeChanger !== 'undefined') { return showSizeChanger; } return total > totalBoundaryShowSizeChanger; }, runIfEnter(event, callback, ...restParams) { if (event.key === 'Enter' || event.charCode === 13) { callback(...restParams); } }, runIfEnterPrev(event) { this.runIfEnter(event, this.prev); }, runIfEnterNext(event) { this.runIfEnter(event, this.next); }, runIfEnterJumpPrev(event) { this.runIfEnter(event, this.jumpPrev); }, runIfEnterJumpNext(event) { this.runIfEnter(event, this.jumpNext); }, handleGoTO(event) { if (event.keyCode === KEYCODE.ENTER || event.type === 'click') { this.handleChange(this.stateCurrentInputValue); } }, renderPrev(prevPage) { const { itemRender } = this.$props; const prevButton = itemRender({ page: prevPage, type: 'prev', originalElement: this.getItemIcon('prevIcon', 'prev page'), }); const disabled = !this.hasPrev(); return isValidElement(prevButton) ? cloneElement(prevButton, disabled ? { disabled } : {}) : prevButton; }, renderNext(nextPage) { const { itemRender } = this.$props; const nextButton = itemRender({ page: nextPage, type: 'next', originalElement: this.getItemIcon('nextIcon', 'next page'), }); const disabled = !this.hasNext(); return isValidElement(nextButton) ? cloneElement(nextButton, disabled ? { disabled } : {}) : nextButton; }, }, render() { const { prefixCls, disabled, hideOnSinglePage, total, locale, showQuickJumper, showLessItems, showTitle, showTotal, simple, itemRender, showPrevNextJumpers, jumpPrevIcon, jumpNextIcon, selectComponentClass, selectPrefixCls, pageSizeOptions, } = this.$props; const { stateCurrent, statePageSize } = this; const { class: className, ...restAttrs } = splitAttrs(this.$attrs).extraAttrs; // When hideOnSinglePage is true and there is only 1 page, hide the pager if (hideOnSinglePage === true && this.total <= statePageSize) { return null; } const allPages = calculatePage(undefined, this.$data, this.$props); const pagerList = []; let jumpPrev = null; let jumpNext = null; let firstPager = null; let lastPager = null; let gotoButton = null; const goButton = showQuickJumper && showQuickJumper.goButton; const pageBufferSize = showLessItems ? 1 : 2; const prevPage = stateCurrent - 1 > 0 ? stateCurrent - 1 : 0; const nextPage = stateCurrent + 1 < allPages ? stateCurrent + 1 : allPages; const hasPrev = this.hasPrev(); const hasNext = this.hasNext(); if (simple) { if (goButton) { if (typeof goButton === 'boolean') { gotoButton = ( ); } else { gotoButton = ( {goButton} ); } gotoButton = (