feat: 分页组件基础逻辑开发
This commit is contained in:
parent
0a4503c9ea
commit
9ea2a05d19
|
@ -150,6 +150,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 输入框,选择器,文本域 **/
|
/** 输入框,选择器,文本域 **/
|
||||||
|
.arco-select {
|
||||||
|
.arco-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
}
|
||||||
.arco-input-wrapper,
|
.arco-input-wrapper,
|
||||||
.arco-textarea-wrapper,
|
.arco-textarea-wrapper,
|
||||||
.arco-input-tag,
|
.arco-input-tag,
|
||||||
|
@ -483,3 +489,25 @@
|
||||||
.arco-switch-checked {
|
.arco-switch-checked {
|
||||||
background: rgb(var(--primary-6)) !important;
|
background: rgb(var(--primary-6)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 分页 **/
|
||||||
|
.arco-pagination-total {
|
||||||
|
color: var(--color-text-2) !important;
|
||||||
|
}
|
||||||
|
.arco-pagination-options {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
.arco-pagination-total {
|
||||||
|
margin-right: 16px !important;
|
||||||
|
}
|
||||||
|
.arco-pagination-item-previous {
|
||||||
|
margin-left: 14px !important;
|
||||||
|
}
|
||||||
|
.arco-pagination-size-small .arco-pagination-item {
|
||||||
|
border: 1px solid var(--color-text-input-border);
|
||||||
|
}
|
||||||
|
.arco-pagination-item-active {
|
||||||
|
border-color: rgb(var(--primary-5)) !important;
|
||||||
|
color: rgb(var(--primary-5)) !important;
|
||||||
|
background-color: rgb(var(--primary-1)) !important;
|
||||||
|
}
|
||||||
|
|
|
@ -17,3 +17,9 @@
|
||||||
extraProps: { ...props },
|
extraProps: { ...props },
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.arco-icon {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import type { App } from 'vue';
|
||||||
|
import type { ArcoOptions } from './types';
|
||||||
|
import { setGlobalConfig, getComponentPrefix } from './utils';
|
||||||
|
import _Pagination from './pagination';
|
||||||
|
|
||||||
|
const MsPagination = Object.assign(_Pagination, {
|
||||||
|
install: (app: App, options?: ArcoOptions) => {
|
||||||
|
setGlobalConfig(app, options);
|
||||||
|
const componentPrefix = getComponentPrefix(options);
|
||||||
|
|
||||||
|
app.component(componentPrefix + _Pagination.name, _Pagination);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PaginationInstance = InstanceType<typeof _Pagination>;
|
||||||
|
export type { PaginationProps } from './interface';
|
||||||
|
|
||||||
|
export default MsPagination;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { CSSProperties } from 'vue';
|
||||||
|
import { Size } from './types';
|
||||||
|
import { SelectProps } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
export const PAGE_ITEM_TYPES = ['page', 'more', 'previous', 'next'] as const;
|
||||||
|
|
||||||
|
export type PageItemType = (typeof PAGE_ITEM_TYPES)[number];
|
||||||
|
|
||||||
|
export interface PaginationProps {
|
||||||
|
total?: number;
|
||||||
|
current?: number;
|
||||||
|
defaultCurrent?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
defaultPageSize?: number;
|
||||||
|
disabled?: boolean;
|
||||||
|
hideOnSinglePage?: boolean;
|
||||||
|
simple?: boolean;
|
||||||
|
showTotal?: boolean;
|
||||||
|
showMore?: boolean;
|
||||||
|
showJumper?: boolean;
|
||||||
|
showPageSize?: boolean;
|
||||||
|
pageSizeOptions?: number[];
|
||||||
|
pageSizeProps?: SelectProps;
|
||||||
|
size?: Size;
|
||||||
|
pageItemStyle?: CSSProperties;
|
||||||
|
activePageItemStyle?: CSSProperties;
|
||||||
|
baseSize?: number;
|
||||||
|
bufferSize?: number;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
export default {
|
||||||
|
msPagination: {
|
||||||
|
total: 'A total of {total} items',
|
||||||
|
current: 'Current {current}',
|
||||||
|
pageSize: 'Page size',
|
||||||
|
goto: 'Goto',
|
||||||
|
page: 'Page',
|
||||||
|
countPerPage: ' / Page',
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
export default {
|
||||||
|
msPagination: {
|
||||||
|
total: '共 {total} 项数据',
|
||||||
|
current: '当前页数 {current}',
|
||||||
|
countPerPage: '条/页',
|
||||||
|
pageSize: '每页条数',
|
||||||
|
goto: '前往',
|
||||||
|
page: '页',
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
<template>
|
||||||
|
<li :class="cls" @click="handleClick">
|
||||||
|
<slot>
|
||||||
|
<MsIcon type="icon-icon_more_outlined" />
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent } from 'vue';
|
||||||
|
import { getPrefixCls, getLegalPage } from './utils';
|
||||||
|
import MsIcon from '../ms-icon-font/index.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'EllipsisPager',
|
||||||
|
components: {
|
||||||
|
MsIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
current: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
step: {
|
||||||
|
type: Number,
|
||||||
|
default: 5,
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['click'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const prefixCls = getPrefixCls('pagination-item');
|
||||||
|
|
||||||
|
const nextPage = computed(() =>
|
||||||
|
getLegalPage(props.current + props.step, {
|
||||||
|
min: 1,
|
||||||
|
max: props.pages,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const handleClick = (e: MouseEvent) => {
|
||||||
|
emit('click', nextPage.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cls = computed(() => [prefixCls, `${prefixCls}-ellipsis`]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefixCls,
|
||||||
|
cls,
|
||||||
|
handleClick,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<component :is="simple ? 'span' : 'li'" :class="cls" @click="handleClick">
|
||||||
|
<slot :type="isNext ? 'next' : 'previous'">
|
||||||
|
<MsIcon v-if="isNext" type="icon-icon_right_outlined" />
|
||||||
|
<MsIcon v-else type="icon-icon_left_outlined" />
|
||||||
|
</slot>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent } from 'vue';
|
||||||
|
import { getPrefixCls, getLegalPage } from './utils';
|
||||||
|
import MsIcon from '../ms-icon-font/index.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'StepPager',
|
||||||
|
components: {
|
||||||
|
MsIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pages: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
current: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
simple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['click'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const prefixCls = getPrefixCls('pagination-item');
|
||||||
|
const isNext = props.type === 'next';
|
||||||
|
const mergedDisabled = computed(() => {
|
||||||
|
if (props.disabled) {
|
||||||
|
return props.disabled;
|
||||||
|
}
|
||||||
|
if (!props.pages) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isNext && props.current === props.pages) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !isNext && props.current <= 1;
|
||||||
|
});
|
||||||
|
const nextPage = computed(() =>
|
||||||
|
getLegalPage(props.current + (isNext ? 1 : -1), {
|
||||||
|
min: 1,
|
||||||
|
max: props.pages,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const handleClick = (e: MouseEvent) => {
|
||||||
|
if (!mergedDisabled.value) {
|
||||||
|
emit('click', nextPage.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cls = computed(() => [
|
||||||
|
prefixCls,
|
||||||
|
`${prefixCls}-${props.type}`,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-disabled`]: mergedDisabled.value,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefixCls,
|
||||||
|
cls,
|
||||||
|
isNext,
|
||||||
|
handleClick,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<li :class="cls" :style="mergedStyle" @click="handleClick">
|
||||||
|
<slot :page="pageNumber">
|
||||||
|
{{ pageNumber }}
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PropType, CSSProperties } from 'vue';
|
||||||
|
import { computed, defineComponent } from 'vue';
|
||||||
|
import { getPrefixCls } from './utils';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Pager',
|
||||||
|
props: {
|
||||||
|
pageNumber: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
current: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
type: Object as PropType<CSSProperties>,
|
||||||
|
},
|
||||||
|
activeStyle: {
|
||||||
|
type: Object as PropType<CSSProperties>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['click'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const prefixCls = getPrefixCls('pagination-item');
|
||||||
|
const isActive = computed(() => props.current === props.pageNumber);
|
||||||
|
|
||||||
|
const handleClick = (e: MouseEvent) => {
|
||||||
|
if (!props.disabled) {
|
||||||
|
emit('click', props.pageNumber, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cls = computed(() => [
|
||||||
|
prefixCls,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-active`]: isActive.value,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const mergedStyle = computed(() => {
|
||||||
|
return isActive.value ? props.activeStyle : props.style;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefixCls,
|
||||||
|
cls,
|
||||||
|
mergedStyle,
|
||||||
|
handleClick,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<span :class="cls">
|
||||||
|
<span v-if="!simple" :class="[`${prefixCls}-prepend`, `${prefixCls}-text-goto`]">
|
||||||
|
<slot name="jumper-prepend">{{ t('msPagination.goto') }}</slot>
|
||||||
|
</span>
|
||||||
|
<a-input-number
|
||||||
|
v-model="inputValue"
|
||||||
|
:class="`${prefixCls}-input`"
|
||||||
|
:min="1"
|
||||||
|
:max="pages"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
hide-button
|
||||||
|
:formatter="handleFormatter"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
<span v-if="$slots['jumper-append']" :class="`${prefixCls}-append`"><slot name="jumper-append" /></span>
|
||||||
|
<template v-if="simple">
|
||||||
|
<span :class="`${prefixCls}-separator`">/</span>
|
||||||
|
<span :class="`${prefixCls}-total-page`">{{ pages }}</span>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, nextTick, PropType, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { getPrefixCls } from './utils';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'PageJumper',
|
||||||
|
props: {
|
||||||
|
current: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
simple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<'small' | 'mini' | 'medium' | 'large' | undefined>,
|
||||||
|
},
|
||||||
|
onChange: {
|
||||||
|
type: Function as PropType<(value: number) => void>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['change'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const prefixCls = getPrefixCls('pagination-jumper');
|
||||||
|
const { t } = useI18n();
|
||||||
|
const inputValue = ref(props.simple ? props.current : undefined);
|
||||||
|
|
||||||
|
const handleFormatter = (value: number) => {
|
||||||
|
const parseIntVal = parseInt(value.toString(), 10);
|
||||||
|
return Number.isNaN(parseIntVal) ? undefined : String(parseIntVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const handleChange = (value: number | undefined) => {
|
||||||
|
emit('change', inputValue.value);
|
||||||
|
nextTick(() => {
|
||||||
|
if (!props.simple) {
|
||||||
|
inputValue.value = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.current,
|
||||||
|
(value) => {
|
||||||
|
if (props.simple && value !== inputValue.value) {
|
||||||
|
inputValue.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const cls = computed(() => [
|
||||||
|
prefixCls,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-simple`]: props.simple,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefixCls,
|
||||||
|
cls,
|
||||||
|
t,
|
||||||
|
inputValue,
|
||||||
|
handleChange,
|
||||||
|
handleFormatter,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<template>
|
||||||
|
<span :class="prefixCls">
|
||||||
|
<a-select
|
||||||
|
:model-value="pageSize"
|
||||||
|
:options="options"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
v-bind="selectProps"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { getPrefixCls } from './utils';
|
||||||
|
import { Size } from './types';
|
||||||
|
import { SelectProps } from '@arco-design/web-vue/es/select/interface';
|
||||||
|
|
||||||
|
defineOptions({ name: 'PageOptions' });
|
||||||
|
|
||||||
|
interface PageOptionsProps {
|
||||||
|
sizeOptions: number[];
|
||||||
|
pageSize: number;
|
||||||
|
disabled: boolean;
|
||||||
|
size: Size;
|
||||||
|
onChange: (value: number) => void;
|
||||||
|
selectProps?: SelectProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefixCls = getPrefixCls('pagination-options');
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'change', value: number): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const props = defineProps<PageOptionsProps>();
|
||||||
|
|
||||||
|
const handleChange = (value: string | number | Record<string, any> | (string | number | Record<string, any>)[]) => {
|
||||||
|
emit('change', value as number);
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = computed(() =>
|
||||||
|
props.sizeOptions.map((value) => ({
|
||||||
|
value,
|
||||||
|
label: `${value} ${t('msPagination.countPerPage')}`,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
</script>
|
|
@ -0,0 +1,428 @@
|
||||||
|
import type { PropType, CSSProperties } from 'vue';
|
||||||
|
import { computed, defineComponent, reactive, ref, toRefs, watch } from 'vue';
|
||||||
|
import { getPrefixCls, isNumber } from './utils';
|
||||||
|
import { Size } from './types';
|
||||||
|
import Pager from './page-item.vue';
|
||||||
|
import StepPager from './page-item-step.vue';
|
||||||
|
import EllipsisPager from './page-item-ellipsis.vue';
|
||||||
|
import PageJumper from './page-jumper.vue';
|
||||||
|
import PageOptions from './page-options.vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import type { PageItemType } from './interface';
|
||||||
|
import { SelectProps } from '@arco-design/web-vue/es/select/interface';
|
||||||
|
import useSize from './useSize';
|
||||||
|
|
||||||
|
export type Data = Record<string, any>;
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'MsPagination',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* @zh 数据总数
|
||||||
|
* @en Total number of data
|
||||||
|
*/
|
||||||
|
total: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 当前的页数
|
||||||
|
* @en Current page number
|
||||||
|
* @vModel
|
||||||
|
*/
|
||||||
|
current: Number,
|
||||||
|
/**
|
||||||
|
* @zh 默认的页数(非受控状态)
|
||||||
|
* @en The default number of pages (uncontrolled state)
|
||||||
|
*/
|
||||||
|
defaultCurrent: {
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 每页展示的数据条数
|
||||||
|
* @en Number of data items displayed per page
|
||||||
|
* @vModel
|
||||||
|
*/
|
||||||
|
pageSize: Number,
|
||||||
|
/**
|
||||||
|
* @zh 默认每页展示的数据条数(非受控状态)
|
||||||
|
* @en The number of data items displayed per page by default (uncontrolled state)
|
||||||
|
*/
|
||||||
|
defaultPageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否禁用
|
||||||
|
* @en Whether to disable
|
||||||
|
*/
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 单页时是否隐藏分页
|
||||||
|
* @en Whether to hide pagination when single page
|
||||||
|
*/
|
||||||
|
hideOnSinglePage: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否为简单模式
|
||||||
|
* @en Whether it is simple mode
|
||||||
|
*/
|
||||||
|
simple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否显示数据总数
|
||||||
|
* @en Whether to display the total number of data
|
||||||
|
*/
|
||||||
|
showTotal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否显示更多按钮
|
||||||
|
* @en Whether to show more buttons
|
||||||
|
*/
|
||||||
|
showMore: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否显示跳转
|
||||||
|
* @en Whether to show jump
|
||||||
|
*/
|
||||||
|
showJumper: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否显示数据条数选择器
|
||||||
|
* @en Whether to display the data number selector
|
||||||
|
*/
|
||||||
|
showPageSize: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 数据条数选择器的选项列表
|
||||||
|
* @en Selection list of data number selector
|
||||||
|
*/
|
||||||
|
pageSizeOptions: {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
default: () => [10, 20, 30, 40, 50],
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 数据条数选择器的Props
|
||||||
|
* @en Props of data number selector
|
||||||
|
*/
|
||||||
|
pageSizeProps: {
|
||||||
|
type: Object as PropType<SelectProps>,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 分页选择器的大小
|
||||||
|
* @en The size of the page selector
|
||||||
|
* @values 'mini', 'small', 'medium', 'large'
|
||||||
|
* @defaultValue 'medium'
|
||||||
|
*/
|
||||||
|
size: {
|
||||||
|
type: String as PropType<Size>,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 分页按钮的样式
|
||||||
|
* @en The style of the paging button
|
||||||
|
*/
|
||||||
|
pageItemStyle: {
|
||||||
|
type: Object as PropType<CSSProperties>,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 当前分页按钮的样式
|
||||||
|
* @en The style of the current paging button
|
||||||
|
*/
|
||||||
|
activePageItemStyle: {
|
||||||
|
type: Object as PropType<CSSProperties>,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 计算显示省略的基础个数。显示省略的个数为 `baseSize + 2 * bufferSize`
|
||||||
|
* @en Calculate and display the number of omitted bases. Display the omitted number as `baseSize + 2 * bufferSize`
|
||||||
|
*/
|
||||||
|
baseSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 6,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 显示省略号时,当前页码左右显示的页码个数
|
||||||
|
* @en When the ellipsis is displayed, the number of page numbers displayed on the left and right of the current page number
|
||||||
|
*/
|
||||||
|
bufferSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 2,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 是否在改变数据条数时调整页码
|
||||||
|
* @en Whether to adjust the page number when changing the number of data
|
||||||
|
* @version 2.34.0
|
||||||
|
*/
|
||||||
|
autoAdjust: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
'update:current': (current: number) => true,
|
||||||
|
'update:pageSize': (pageSize: number) => true,
|
||||||
|
/**
|
||||||
|
* @zh 页码改变时触发
|
||||||
|
* @en Triggered when page number changes
|
||||||
|
* @param {number} current
|
||||||
|
*/
|
||||||
|
'change': (current: number) => true,
|
||||||
|
/**
|
||||||
|
* @zh 数据条数改变时触发
|
||||||
|
* @en Triggered when the number of data items changes
|
||||||
|
* @param {number} pageSize
|
||||||
|
*/
|
||||||
|
'pageSizeChange': (pageSize: number) => true,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @zh 分页按钮
|
||||||
|
* @en Page item
|
||||||
|
* @version 2.9.0
|
||||||
|
* @slot page-item
|
||||||
|
* @binding {number} page The page number of the paging button
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @zh 分页按钮(步)
|
||||||
|
* @en Page item (step)
|
||||||
|
* @version 2.9.0
|
||||||
|
* @slot page-item-step
|
||||||
|
* @binding {'previous'|'next'} type The type of page item step
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @zh 分页按钮(省略)
|
||||||
|
* @en Page item (ellipsis)
|
||||||
|
* @version 2.9.0
|
||||||
|
* @slot page-item-ellipsis
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @zh 总数
|
||||||
|
* @en Total
|
||||||
|
* @version 2.9.0
|
||||||
|
* @slot total
|
||||||
|
* @binding {number} total
|
||||||
|
*/
|
||||||
|
setup(props, { emit, slots }) {
|
||||||
|
const prefixCls = getPrefixCls('pagination');
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { disabled, pageItemStyle, activePageItemStyle, size } = toRefs(props);
|
||||||
|
const { mergedSize } = useSize(size);
|
||||||
|
|
||||||
|
const _current = ref(props.defaultCurrent);
|
||||||
|
const _pageSize = ref(props.defaultPageSize);
|
||||||
|
const computedCurrent = computed(() => props.current ?? _current.value);
|
||||||
|
const computedPageSize = computed(() => props.pageSize ?? _pageSize.value);
|
||||||
|
|
||||||
|
const pages = computed(() => Math.ceil(props.total / computedPageSize.value));
|
||||||
|
|
||||||
|
const handleClick = (page: number) => {
|
||||||
|
// when pageJumper blur and input.value is undefined, page is illegal
|
||||||
|
if (page !== computedCurrent.value && isNumber(page) && !props.disabled) {
|
||||||
|
_current.value = page;
|
||||||
|
emit('update:current', page);
|
||||||
|
emit('change', page);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageSizeChange = (pageSize: number) => {
|
||||||
|
_pageSize.value = pageSize;
|
||||||
|
emit('update:pageSize', pageSize);
|
||||||
|
emit('pageSizeChange', pageSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pagerProps = reactive({
|
||||||
|
current: computedCurrent,
|
||||||
|
pages,
|
||||||
|
disabled,
|
||||||
|
style: pageItemStyle,
|
||||||
|
activeStyle: activePageItemStyle,
|
||||||
|
onClick: handleClick,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getPageItemElement = (type: PageItemType, prop: Data = {}) => {
|
||||||
|
if (type === 'more') {
|
||||||
|
return <EllipsisPager v-slots={{ default: slots['page-item-ellipsis'] }} {...prop} {...pagerProps} />;
|
||||||
|
}
|
||||||
|
if (type === 'previous') {
|
||||||
|
return <StepPager v-slots={{ default: slots['page-item-step'] }} type="previous" {...prop} {...pagerProps} />;
|
||||||
|
}
|
||||||
|
if (type === 'next') {
|
||||||
|
return <StepPager v-slots={{ default: slots['page-item-step'] }} type="next" {...prop} {...pagerProps} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Pager v-slots={{ default: slots['page-item'] }} {...prop} {...pagerProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageList = computed(() => {
|
||||||
|
const pageListArr: Array<JSX.Element | JSX.Element[]> = [];
|
||||||
|
|
||||||
|
if (pages.value < props.baseSize + props.bufferSize * 2) {
|
||||||
|
for (let i = 1; i <= pages.value; i++) {
|
||||||
|
pageListArr.push(getPageItemElement('page', { key: i, pageNumber: i }));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let left = 1;
|
||||||
|
let right = pages.value;
|
||||||
|
let hasLeftEllipsis = false;
|
||||||
|
let hasRightEllipsis = false;
|
||||||
|
|
||||||
|
if (computedCurrent.value > 2 + props.bufferSize) {
|
||||||
|
hasLeftEllipsis = true;
|
||||||
|
left = Math.min(computedCurrent.value - props.bufferSize, pages.value - 2 * props.bufferSize);
|
||||||
|
}
|
||||||
|
if (computedCurrent.value < pages.value - (props.bufferSize + 1)) {
|
||||||
|
hasRightEllipsis = true;
|
||||||
|
right = Math.max(computedCurrent.value + props.bufferSize, 2 * props.bufferSize + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLeftEllipsis) {
|
||||||
|
pageListArr.push(getPageItemElement('page', { key: 1, pageNumber: 1 }));
|
||||||
|
pageListArr.push(
|
||||||
|
getPageItemElement('more', {
|
||||||
|
key: 'left-ellipsis-pager',
|
||||||
|
step: -(props.bufferSize * 2 + 1),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = left; i <= right; i++) {
|
||||||
|
pageListArr.push(getPageItemElement('page', { key: i, pageNumber: i }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasRightEllipsis) {
|
||||||
|
pageListArr.push(
|
||||||
|
getPageItemElement('more', {
|
||||||
|
key: 'right-ellipsis-pager',
|
||||||
|
step: props.bufferSize * 2 + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
pageListArr.push(
|
||||||
|
getPageItemElement('page', {
|
||||||
|
key: pages.value,
|
||||||
|
pageNumber: pages.value,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageListArr;
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderPager = () => {
|
||||||
|
if (props.simple) {
|
||||||
|
return (
|
||||||
|
<span class={`${prefixCls}-simple`}>
|
||||||
|
{getPageItemElement('previous', { simple: true })}
|
||||||
|
<PageJumper
|
||||||
|
disabled={props.disabled}
|
||||||
|
current={computedCurrent.value}
|
||||||
|
size={mergedSize.value}
|
||||||
|
pages={pages.value}
|
||||||
|
simple
|
||||||
|
onChange={handleClick}
|
||||||
|
/>
|
||||||
|
{getPageItemElement('next', { simple: true })}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul class={`${prefixCls}-list`}>
|
||||||
|
{getPageItemElement('previous', { simple: true })}
|
||||||
|
{pageList.value}
|
||||||
|
{props.showMore &&
|
||||||
|
getPageItemElement('more', {
|
||||||
|
key: 'more',
|
||||||
|
step: props.bufferSize * 2 + 1,
|
||||||
|
})}
|
||||||
|
{getPageItemElement('next', { simple: true })}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// When the number of data items changes, recalculate the page number
|
||||||
|
watch(computedPageSize, (curPageSize, prePageSize) => {
|
||||||
|
if (props.autoAdjust && curPageSize !== prePageSize && computedCurrent.value > 1) {
|
||||||
|
const index = prePageSize * (computedCurrent.value - 1) + 1;
|
||||||
|
const newPage = Math.ceil(index / curPageSize);
|
||||||
|
if (newPage !== computedCurrent.value) {
|
||||||
|
_current.value = newPage;
|
||||||
|
emit('update:current', newPage);
|
||||||
|
emit('change', newPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(pages, (curPages, prePages) => {
|
||||||
|
if (props.autoAdjust && curPages !== prePages && computedCurrent.value > 1 && computedCurrent.value > curPages) {
|
||||||
|
_current.value = curPages;
|
||||||
|
emit('update:current', curPages);
|
||||||
|
emit('change', curPages);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cls = computed(() => [
|
||||||
|
prefixCls,
|
||||||
|
`${prefixCls}-size-${mergedSize.value}`,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-simple`]: props.simple,
|
||||||
|
[`${prefixCls}-disabled`]: props.disabled,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (props.hideOnSinglePage && pages.value <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={cls.value}>
|
||||||
|
{props.showTotal && (
|
||||||
|
<span class={`${prefixCls}-total`}>
|
||||||
|
{slots.total?.({ total: props.total }) ?? t('msPagination.total', { total: props.total })}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{props.showPageSize && (
|
||||||
|
<PageOptions
|
||||||
|
disabled={props.disabled}
|
||||||
|
sizeOptions={props.pageSizeOptions}
|
||||||
|
pageSize={computedPageSize.value}
|
||||||
|
size={mergedSize.value}
|
||||||
|
onChange={(v: number) => handlePageSizeChange(v)}
|
||||||
|
selectProps={props.pageSizeProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{renderPager()}
|
||||||
|
{!props.simple && props.showJumper && (
|
||||||
|
<PageJumper
|
||||||
|
v-slots={{
|
||||||
|
'jumper-prepend': slots['jumper-prepend'],
|
||||||
|
'jumper-append': slots['jumper-append'],
|
||||||
|
}}
|
||||||
|
disabled={props.disabled}
|
||||||
|
current={computedCurrent.value}
|
||||||
|
pages={pages.value}
|
||||||
|
size={mergedSize.value}
|
||||||
|
onChange={handleClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Slots } from 'vue';
|
||||||
|
import { ArcoLang } from '@arco-design/web-vue/es/locale/interface';
|
||||||
|
|
||||||
|
export interface ArcoOptions {
|
||||||
|
classPrefix?: string;
|
||||||
|
componentPrefix?: string;
|
||||||
|
}
|
||||||
|
export const SIZES = ['mini', 'small', 'medium', 'large'] as const;
|
||||||
|
|
||||||
|
export type Size = (typeof SIZES)[number];
|
||||||
|
|
||||||
|
export interface ConfigProvider {
|
||||||
|
slots: Slots;
|
||||||
|
prefixCls?: string;
|
||||||
|
locale?: ArcoLang;
|
||||||
|
size?: Size;
|
||||||
|
updateAtScroll?: boolean;
|
||||||
|
scrollToClose?: boolean;
|
||||||
|
exchangeTime?: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { computed, inject, Ref } from 'vue';
|
||||||
|
import { Size } from './types';
|
||||||
|
import { configProviderInjectionKey } from './utils';
|
||||||
|
|
||||||
|
const useSize = (size?: Ref<Size | undefined>, { defaultValue = 'medium' }: { defaultValue?: Size } = {}) => {
|
||||||
|
const configProviderCtx = inject(configProviderInjectionKey, undefined);
|
||||||
|
|
||||||
|
const mergedSize = computed(() => size?.value ?? configProviderCtx?.size ?? defaultValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mergedSize,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSize;
|
|
@ -0,0 +1,50 @@
|
||||||
|
import type { App } from 'vue';
|
||||||
|
import { getCurrentInstance, inject, InjectionKey } from 'vue';
|
||||||
|
import type { ArcoOptions, ConfigProvider } from './types';
|
||||||
|
|
||||||
|
const COMPONENT_PREFIX = 'A';
|
||||||
|
const CLASS_PREFIX = 'arco';
|
||||||
|
const GLOBAL_CONFIG_NAME = '$arco';
|
||||||
|
|
||||||
|
export const configProviderInjectionKey: InjectionKey<ConfigProvider> = Symbol('ArcoConfigProvider');
|
||||||
|
|
||||||
|
export const getComponentPrefix = (options?: ArcoOptions) => {
|
||||||
|
return options?.componentPrefix ?? COMPONENT_PREFIX;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setGlobalConfig = (app: App, options?: ArcoOptions): void => {
|
||||||
|
if (options && options.classPrefix) {
|
||||||
|
app.config.globalProperties[GLOBAL_CONFIG_NAME] = {
|
||||||
|
...(app.config.globalProperties[GLOBAL_CONFIG_NAME] ?? {}),
|
||||||
|
classPrefix: options.classPrefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPrefixCls = (componentName?: string): string => {
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
const configProvider = inject(configProviderInjectionKey, undefined);
|
||||||
|
|
||||||
|
const prefix =
|
||||||
|
configProvider?.prefixCls ??
|
||||||
|
instance?.appContext.config.globalProperties[GLOBAL_CONFIG_NAME]?.classPrefix ??
|
||||||
|
CLASS_PREFIX;
|
||||||
|
if (componentName) {
|
||||||
|
return `${prefix}-${componentName}`;
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLegalPage = (page: number, { min, max }: { min: number; max: number }): number => {
|
||||||
|
if (page < min) {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
if (page > max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
return page;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isNumber(obj: any): obj is number {
|
||||||
|
return Object.prototype.toString.call(obj) === '[object Number]' && obj === obj; // eslint-disable-line
|
||||||
|
}
|
|
@ -75,7 +75,7 @@
|
||||||
} from './type';
|
} from './type';
|
||||||
import BatchAction from './batchAction.vue';
|
import BatchAction from './batchAction.vue';
|
||||||
|
|
||||||
import type { TableColumnData, TableData } from '@arco-design/web-vue';
|
import type { TableData } from '@arco-design/web-vue';
|
||||||
import ColumnSelector from './columnSelector.vue';
|
import ColumnSelector from './columnSelector.vue';
|
||||||
|
|
||||||
const batchleft = ref('10px');
|
const batchleft = ref('10px');
|
||||||
|
|
|
@ -1,3 +1,31 @@
|
||||||
<template> BugManagement is waiting for development </template>
|
<template>
|
||||||
|
<div>BugManagement is waiting for development </div>
|
||||||
|
<div class="continer">
|
||||||
|
<ms-pagination
|
||||||
|
v-modal:page-size="pageSize"
|
||||||
|
size="small"
|
||||||
|
:total="100"
|
||||||
|
show-total
|
||||||
|
show-jumper
|
||||||
|
show-more
|
||||||
|
show-page-size
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import MsPagination from '@/components/pure/ms-pagination/index';
|
||||||
|
|
||||||
|
const pageSize = ref(10);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.continer {
|
||||||
|
margin-top: 200px;
|
||||||
|
padding: 50px;
|
||||||
|
width: 100%;
|
||||||
|
height: 500px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue