You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ant-design-vue/components/vc-pagination/Pagination.jsx

577 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;
}
function calculatePage(p, state, props) {
let pageSize = p;
if (typeof pageSize === 'undefined') {
pageSize = state.statePageSize;
}
return Math.floor((props.total - 1) / pageSize) + 1;
}
export default {
name: 'Pagination',
mixins: [BaseMixin],
model: {
prop: 'current',
event: 'change.current',
},
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,
},
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 = 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();
}
}
});
},
},
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) {
const { prefixCls } = this.$props;
const iconNode = getComponentFromProp(this, icon, this.$props) || (
<a class={`${prefixCls}-item-link`} />
);
return iconNode;
},
isValid(page) {
return isInteger(page) && page >= 1 && page !== this.stateCurrent;
},
// 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(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 = 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);
this.$emit('showSizeChange', current, size);
if (current !== preCurrent) {
this.$emit('change.current', current, size);
}
},
handleChange(p) {
let page = p;
if (this.isValid(page)) {
const currentPage = calculatePage(undefined, this.$data, this.$props);
if (page > currentPage) {
page = currentPage;
}
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;
},
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);
},
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);
}
},
},
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 = 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 = 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 = (
<button type="button" onClick={this.handleGoTO} onKeyup={this.handleGoTO}>
{locale.jump_to_confirm}
</button>
);
} else {
gotoButton = (
<span onClick={this.handleGoTO} onKeyup={this.handleGoTO}>
{goButton}
</span>
);
}
gotoButton = (
<li
title={this.showTitle ? `${locale.jump_to}${this.stateCurrent}/${allPages}` : null}
class={`${prefixCls}-simple-pager`}
>
{gotoButton}
</li>
);
}
const hasPrev = this.hasPrev();
const hasNext = this.hasNext();
return (
<ul class={`${prefixCls} ${prefixCls}-simple`}>
<li
title={this.showTitle ? locale.prev_page : null}
onClick={this.prev}
tabIndex={hasPrev ? 0 : null}
onKeypress={this.runIfEnterPrev}
class={`${hasPrev ? '' : `${prefixCls}-disabled`} ${prefixCls}-prev`}
aria-disabled={!this.hasPrev()}
>
{this.itemRender(prevPage, 'prev', this.getItemIcon('prevIcon'))}
</li>
<li
title={this.showTitle ? `${stateCurrent}/${allPages}` : null}
class={`${prefixCls}-simple-pager`}
>
<input
type="text"
value={this.stateCurrentInputValue}
onKeydown={this.handleKeyDown}
onKeyup={this.handleKeyUp}
onInput={this.handleKeyUp}
size="3"
/>
<span class={`${prefixCls}-slash`}></span>
{allPages}
</li>
<li
title={this.showTitle ? locale.next_page : null}
onClick={this.next}
tabIndex={this.hasNext ? 0 : null}
onKeypress={this.runIfEnterNext}
class={`${hasNext ? '' : `${prefixCls}-disabled`} ${prefixCls}-next`}
aria-disabled={!this.hasNext()}
>
{this.itemRender(nextPage, 'next', this.getItemIcon('nextIcon'))}
</li>
{gotoButton}
</ul>
);
}
if (allPages <= 5 + pageBufferSize * 2) {
const pagerProps = {
props: {
locale,
rootPrefixCls: prefixCls,
showTitle: props.showTitle,
itemRender: props.itemRender,
},
on: {
click: this.handleChange,
keypress: this.runIfEnter,
},
};
if (!allPages) {
pagerList.push(
<Pager {...pagerProps} key="noPager" page={allPages} class={`${prefixCls}-disabled`} />,
);
}
for (let i = 1; i <= allPages; i++) {
const active = stateCurrent === i;
pagerList.push(<Pager {...pagerProps} key={i} page={i} active={active} />);
}
} else {
const prevItemTitle = this.showLessItems ? locale.prev_3 : locale.prev_5;
const nextItemTitle = this.showLessItems ? locale.next_3 : locale.next_5;
if (this.showPrevNextJumpers) {
let jumpPrevClassString = `${prefixCls}-jump-prev`;
if (props.jumpPrevIcon) {
jumpPrevClassString += ` ${prefixCls}-jump-prev-custom-icon`;
}
jumpPrev = (
<li
title={this.showTitle ? prevItemTitle : null}
key="prev"
onClick={this.jumpPrev}
tabIndex="0"
onKeypress={this.runIfEnterJumpPrev}
class={jumpPrevClassString}
>
{this.itemRender(this.getJumpPrevPage(), 'jump-prev', this.getItemIcon('jumpPrevIcon'))}
</li>
);
let jumpNextClassString = `${prefixCls}-jump-next`;
if (props.jumpNextIcon) {
jumpNextClassString += ` ${prefixCls}-jump-next-custom-icon`;
}
jumpNext = (
<li
title={this.showTitle ? nextItemTitle : null}
key="next"
tabIndex="0"
onClick={this.jumpNext}
onKeypress={this.runIfEnterJumpNext}
class={jumpNextClassString}
>
{this.itemRender(this.getJumpNextPage(), 'jump-next', this.getItemIcon('jumpNextIcon'))}
</li>
);
}
lastPager = (
<Pager
locale={locale}
last
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={allPages}
page={allPages}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
firstPager = (
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={1}
page={1}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
let left = Math.max(1, stateCurrent - pageBufferSize);
let right = Math.min(stateCurrent + pageBufferSize, allPages);
if (stateCurrent - 1 <= pageBufferSize) {
right = 1 + pageBufferSize * 2;
}
if (allPages - stateCurrent <= pageBufferSize) {
left = allPages - pageBufferSize * 2;
}
for (let i = left; i <= right; i++) {
const active = stateCurrent === i;
pagerList.push(
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={i}
page={i}
active={active}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>,
);
}
if (stateCurrent - 1 >= pageBufferSize * 2 && stateCurrent !== 1 + 2) {
pagerList[0] = (
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={left}
page={left}
class={`${prefixCls}-item-after-jump-prev`}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
pagerList.unshift(jumpPrev);
}
if (allPages - stateCurrent >= pageBufferSize * 2 && stateCurrent !== allPages - 2) {
pagerList[pagerList.length - 1] = (
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={right}
page={right}
class={`${prefixCls}-item-before-jump-next`}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
pagerList.push(jumpNext);
}
if (left !== 1) {
pagerList.unshift(firstPager);
}
if (right !== allPages) {
pagerList.push(lastPager);
}
}
let totalText = null;
if (this.showTotal) {
totalText = (
<li class={`${prefixCls}-total-text`}>
{this.showTotal(this.total, [
(stateCurrent - 1) * statePageSize + 1,
stateCurrent * statePageSize > this.total ? this.total : stateCurrent * statePageSize,
])}
</li>
);
}
const prevDisabled = !this.hasPrev() || !allPages;
const nextDisabled = !this.hasNext() || !allPages;
const buildOptionText = this.buildOptionText || this.$scopedSlots.buildOptionText;
return (
<ul class={`${prefixCls}`} unselectable="unselectable" ref="paginationNode">
{totalText}
<li
title={this.showTitle ? locale.prev_page : null}
onClick={this.prev}
tabIndex={prevDisabled ? null : 0}
onKeypress={this.runIfEnterPrev}
class={`${!prevDisabled ? '' : `${prefixCls}-disabled`} ${prefixCls}-prev`}
aria-disabled={prevDisabled}
>
{this.itemRender(prevPage, 'prev', this.getItemIcon('prevIcon'))}
</li>
{pagerList}
<li
title={this.showTitle ? locale.next_page : null}
onClick={this.next}
tabIndex={nextDisabled ? null : 0}
onKeypress={this.runIfEnterNext}
class={`${!nextDisabled ? '' : `${prefixCls}-disabled`} ${prefixCls}-next`}
aria-disabled={nextDisabled}
>
{this.itemRender(nextPage, 'next', this.getItemIcon('nextIcon'))}
</li>
<Options
locale={locale}
rootPrefixCls={prefixCls}
selectComponentClass={this.selectComponentClass}
selectPrefixCls={this.selectPrefixCls}
changeSize={this.showSizeChanger ? this.changePageSize : null}
current={stateCurrent}
pageSize={statePageSize}
pageSizeOptions={this.pageSizeOptions}
buildOptionText={buildOptionText || null}
quickGo={this.showQuickJumper ? this.handleChange : null}
goButton={goButton}
/>
</ul>
);
},
};