import PropTypes from '../_util/vue-types' import BaseMixin from '../_util/BaseMixin' import { hasProp, getComponentFromProp } from '../_util/props-util' import Pager from './Pager' import Options from './Options' import LOCALE from './locale/zh_CN' import KEYCODE from './KeyCode' function noop () { } // 是否是正整数 function isInteger (value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value } function defaultItemRender (page, type, element) { return element } export default { name: 'Pagination', mixins: [BaseMixin], props: { prefixCls: PropTypes.string.def('rc-pagination'), selectPrefixCls: PropTypes.string.def('rc-select'), current: PropTypes.number, defaultCurrent: PropTypes.number.def(1), total: PropTypes.number.def(0), pageSize: PropTypes.number, defaultPageSize: PropTypes.number.def(10), hideOnSinglePage: PropTypes.bool.def(false), showSizeChanger: PropTypes.bool.def(false), showLessItems: PropTypes.bool.def(false), // showSizeChange: PropTypes.func.def(noop), selectComponentClass: PropTypes.any, showPrevNextJumpers: PropTypes.bool.def(true), showQuickJumper: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).def(false), showTitle: PropTypes.bool.def(true), pageSizeOptions: PropTypes.arrayOf(PropTypes.string), buildOptionText: PropTypes.func, showTotal: PropTypes.func, simple: PropTypes.bool, locale: PropTypes.object.def(LOCALE), itemRender: PropTypes.func.def(defaultItemRender), prevIcon: PropTypes.any, nextIcon: PropTypes.any, jumpPrevIcon: PropTypes.any, jumpNextIcon: PropTypes.any, }, model: { prop: 'current', event: 'change.current', }, data () { const hasOnChange = this.onChange !== noop const hasCurrent = hasProp(this, 'current') if (hasCurrent && !hasOnChange) { console.warn('Warning: You provided a `current` prop to a Pagination component without an `onChange` handler. This will render a read-only component.'); // eslint-disable-line } let current = this.defaultCurrent if (hasCurrent) { current = this.current } let pageSize = this.defaultPageSize if (hasProp(this, 'pageSize')) { pageSize = this.pageSize } return { stateCurrent: current, stateCurrentInputValue: current, statePageSize: pageSize, } }, watch: { current (val) { this.setState({ stateCurrent: val, stateCurrentInputValue: val, }) }, pageSize (val) { const newState = {} let current = this.stateCurrent const newCurrent = this.calculatePage(val) 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() } } }) }, }, methods: { isValid (page) { return isInteger(page) && page >= 1 && page !== this.stateCurrent }, getItemIcon (icon) { const { prefixCls } = this.$props const iconNode = getComponentFromProp(this, icon, this.$props) || return iconNode }, calculatePage (p) { let pageSize = p if (typeof pageSize === 'undefined') { pageSize = this.statePageSize } return Math.floor((this.total - 1) / pageSize) + 1 }, handleGoTO (event) { if (event.keyCode === KEYCODE.ENTER || event.type === 'click') { this.handleChange(this.stateCurrentInputValue) } }, prev () { if (this.hasPrev()) { this.handleChange(this.stateCurrent - 1) } }, next () { if (this.hasNext()) { this.handleChange(this.stateCurrent + 1) } }, hasPrev () { return this.stateCurrent > 1 }, hasNext () { return this.stateCurrent < this.calculatePage() }, handleKeyDown (event) { if (event.keyCode === KEYCODE.ARROW_UP || event.keyCode === KEYCODE.ARROW_DOWN) { event.preventDefault() } }, handleKeyUp (event) { const inputValue = event.target.value const stateCurrentInputValue = this.stateCurrentInputValue let value if (inputValue === '') { value = inputValue } else if (isNaN(Number(inputValue))) { value = stateCurrentInputValue } else { value = Number(inputValue) } if (value !== stateCurrentInputValue) { this.setState({ stateCurrentInputValue: value, }) } if (event.keyCode === KEYCODE.ENTER) { this.handleChange(value) } else if (event.keyCode === KEYCODE.ARROW_UP) { this.handleChange(value - 1) } else if (event.keyCode === KEYCODE.ARROW_DOWN) { this.handleChange(value + 1) } }, changePageSize (size) { let current = this.stateCurrent const preCurrent = current const newCurrent = this.calculatePage(size) 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) this.$emit('showSizeChange', current, size) if (current !== preCurrent) { this.$emit('change.current', current, size) } }, handleChange (p) { let page = p if (this.isValid(page)) { const allTotal = this.calculatePage() if (page > allTotal) { page = allTotal } if (!hasProp(this, 'current')) { this.setState({ stateCurrent: page, stateCurrentInputValue: page, }) } // this.$emit('input', page) this.$emit('change', page, this.statePageSize) this.$emit('change.current', page, this.statePageSize) return page } return this.stateCurrent }, 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) }, getJumpPrevPage () { return Math.max(1, this.stateCurrent - (this.showLessItems ? 3 : 5)) }, getJumpNextPage () { return Math.min(this.calculatePage(), this.stateCurrent + (this.showLessItems ? 3 : 5)) }, jumpPrev () { this.handleChange(this.getJumpPrevPage()) }, jumpNext () { this.handleChange(this.getJumpNextPage()) }, }, render () { // When hideOnSinglePage is true and there is only 1 page, hide the pager if (this.hideOnSinglePage === true && this.total <= this.statePageSize) { return null } const props = this.$props const locale = this.locale const prefixCls = this.prefixCls const allPages = this.calculatePage() const pagerList = [] let jumpPrev = null let jumpNext = null let firstPager = null let lastPager = null let gotoButton = null const goButton = (this.showQuickJumper && this.showQuickJumper.goButton) const pageBufferSize = this.showLessItems ? 1 : 2 const { stateCurrent, statePageSize } = this const prevPage = stateCurrent - 1 > 0 ? stateCurrent - 1 : 0 const nextPage = stateCurrent + 1 < allPages ? stateCurrent + 1 : allPages if (this.simple) { if (goButton) { if (typeof goButton === 'boolean') { gotoButton = ( ) } else { gotoButton = ( {goButton} ) } gotoButton = (