mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-11-30 02:58:31 +08:00
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-textarea-wrapper,
|
||||
.arco-input-tag,
|
||||
@ -483,3 +489,25 @@
|
||||
.arco-switch-checked {
|
||||
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 },
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.arco-icon {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
</style>
|
||||
|
18
frontend/src/components/pure/ms-pagination/index.ts
Normal file
18
frontend/src/components/pure/ms-pagination/index.ts
Normal file
@ -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;
|
29
frontend/src/components/pure/ms-pagination/interface.ts
Normal file
29
frontend/src/components/pure/ms-pagination/interface.ts
Normal file
@ -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;
|
||||
}
|
10
frontend/src/components/pure/ms-pagination/locale/en-US.ts
Normal file
10
frontend/src/components/pure/ms-pagination/locale/en-US.ts
Normal file
@ -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',
|
||||
},
|
||||
};
|
10
frontend/src/components/pure/ms-pagination/locale/zh-CN.ts
Normal file
10
frontend/src/components/pure/ms-pagination/locale/zh-CN.ts
Normal file
@ -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>
|
64
frontend/src/components/pure/ms-pagination/page-item.vue
Normal file
64
frontend/src/components/pure/ms-pagination/page-item.vue
Normal file
@ -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>
|
103
frontend/src/components/pure/ms-pagination/page-jumper.vue
Normal file
103
frontend/src/components/pure/ms-pagination/page-jumper.vue
Normal file
@ -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>
|
51
frontend/src/components/pure/ms-pagination/page-options.vue
Normal file
51
frontend/src/components/pure/ms-pagination/page-options.vue
Normal file
@ -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>
|
428
frontend/src/components/pure/ms-pagination/pagination.tsx
Normal file
428
frontend/src/components/pure/ms-pagination/pagination.tsx
Normal file
@ -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>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
20
frontend/src/components/pure/ms-pagination/types.ts
Normal file
20
frontend/src/components/pure/ms-pagination/types.ts
Normal file
@ -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;
|
||||
}
|
15
frontend/src/components/pure/ms-pagination/useSize.ts
Normal file
15
frontend/src/components/pure/ms-pagination/useSize.ts
Normal file
@ -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;
|
50
frontend/src/components/pure/ms-pagination/utils.ts
Normal file
50
frontend/src/components/pure/ms-pagination/utils.ts
Normal file
@ -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';
|
||||
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';
|
||||
|
||||
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
Block a user