refactor: refactor pagination component to support display total items (#4303)

#### What type of PR is this?

/area console
/kind improvement
/milestone 2.8.x

#### What this PR does / why we need it:

重构 Console 的分页组件,以支持显示数据总条数。

#### Which issue(s) this PR fixes:

Fixes #4268 

#### Special notes for your reviewer:

需要测试:

- 测试各个页面的分页功能是否正常

#### Does this PR introduce a user-facing change?

```release-note
重构 Console 的分页组件,以支持显示数据总条数。
```
pull/4323/head
Ryan Wang 2023-07-28 11:15:08 +08:00 committed by GitHub
parent 4733008e16
commit 02c47f552a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 167 additions and 117 deletions

View File

@ -8,16 +8,20 @@ const props = withDefaults(
size?: number; size?: number;
total?: number; total?: number;
sizeOptions?: number[]; sizeOptions?: number[];
showTotal?: boolean;
pageLabel?: string; pageLabel?: string;
sizeLabel?: string; sizeLabel?: string;
totalLabel?: string;
}>(), }>(),
{ {
page: 1, page: 1,
size: 10, size: 10,
total: 0, total: 0,
sizeOptions: () => [10], sizeOptions: () => [10],
showTotal: true,
pageLabel: "页", pageLabel: "页",
sizeLabel: "条 / 页", sizeLabel: "条 / 页",
totalLabel: undefined,
} }
); );
@ -33,6 +37,13 @@ const hasNext = computed(() => props.page < totalPages.value);
const hasPrevious = computed(() => props.page > 1); const hasPrevious = computed(() => props.page > 1);
const totalLabelText = computed(() => {
if (props.totalLabel) {
return props.totalLabel;
}
return `${props.total} 项数据`;
});
const onPageChange = (event: Event) => { const onPageChange = (event: Event) => {
const target = event.target as HTMLSelectElement; const target = event.target as HTMLSelectElement;
const page = Number(target.value); const page = Number(target.value);
@ -67,64 +78,45 @@ const next = () => {
}; };
</script> </script>
<template> <template>
<div class="bg-white flex items-center justify-between"> <div class="pagination">
<div class="flex-1 flex justify-between sm:!hidden items-center"> <div v-if="showTotal" class="pagination__total">
<button {{ totalLabelText }}
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 cursor-pointer"
:disabled="!hasPrevious"
@click="previous"
>
<IconArrowLeft />
</button>
<span class="text-sm text-gray-500"> {{ page }} / {{ totalPages }} </span>
<button
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 cursor-pointer"
:disabled="!hasNext"
@click="next"
>
<IconArrowRight />
</button>
</div> </div>
<div class="hidden sm:flex-1 sm:flex sm:items-center items-center gap-2"> <div class="pagination__controller">
<nav <nav aria-label="Pagination" class="pagination__nav">
aria-label="Pagination"
class="relative z-0 inline-flex rounded-base shadow-sm -space-x-px"
>
<button <button
class="relative h-8 outline-none inline-flex items-center px-2 py-1.5 rounded-l-base border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 cursor-pointer disabled:cursor-not-allowed" class="pagination__btn pagination__btn--prev"
:disabled="!hasPrevious" :disabled="!hasPrevious"
@click="previous" @click="previous"
> >
<IconArrowLeft /> <IconArrowLeft />
</button> </button>
<button <button
class="relative h-8 outline-none inline-flex items-center px-2 py-1.5 rounded-r-base border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 cursor-pointer disabled:cursor-not-allowed" class="pagination__btn pagination__btn--next"
:disabled="!hasNext" :disabled="!hasNext"
@click="next" @click="next"
> >
<IconArrowRight /> <IconArrowRight />
</button> </button>
</nav> </nav>
<div class="inline-flex items-center gap-2"> <div class="pagination__select-wrap">
<select <select
:value="page" :value="page"
:disabled="totalPages === 0" :disabled="totalPages === 0"
class="h-8 border outline-none rounded-base px-2 text-gray-800 text-sm border-gray-300" class="pagination__select"
@change="onPageChange" @change="onPageChange"
> >
<option v-if="totalPages === 0" :value="0">0 / 0</option> <option v-if="totalPages === 0" :value="1">0 / 0</option>
<option v-for="i in totalPages" :key="i" :value="i"> <option v-for="i in totalPages || 1" :key="i" :value="i">
{{ i }} / {{ totalPages }} {{ i }} / {{ totalPages }}
</option> </option>
</select> </select>
<span class="text-sm text-gray-500">{{ pageLabel }}</span> <span class="pagination__select-label">
{{ pageLabel }}
</span>
</div> </div>
<div class="inline-flex items-center gap-2"> <div class="pagination__select-wrap">
<select <select :value="size" class="pagination__select" @change="onSizeChange">
:value="size"
class="h-8 border outline-none rounded-base px-2 text-gray-800 text-sm border-gray-300"
@change="onSizeChange"
>
<option <option
v-for="(sizeOption, index) in sizeOptions" v-for="(sizeOption, index) in sizeOptions"
:key="index" :key="index"
@ -133,8 +125,52 @@ const next = () => {
{{ sizeOption }} {{ sizeOption }}
</option> </option>
</select> </select>
<span class="text-sm text-gray-500">{{ sizeLabel }}</span> <span class="pagination__select-label">
{{ sizeLabel }}
</span>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style lang="scss">
.pagination {
@apply bg-white flex items-center flex-1 gap-2;
&__total {
@apply hidden sm:block text-sm text-gray-500;
}
&__controller {
@apply flex items-center gap-2 flex-1 justify-end;
}
&__nav {
@apply relative z-0 inline-flex rounded-base shadow-sm -space-x-px;
}
&__btn {
@apply relative h-8 outline-none inline-flex items-center px-2 py-1.5 rounded-base border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 cursor-pointer disabled:cursor-not-allowed;
&--prev {
@apply rounded-r-none;
}
&--next {
@apply rounded-l-none;
}
}
&__select-wrap {
@apply inline-flex items-center gap-2;
}
&__select {
@apply h-8 border outline-none rounded-base px-2 text-gray-800 text-sm border-gray-300;
}
&__select-label {
@apply text-sm text-gray-500;
}
}
</style>

View File

@ -1149,6 +1149,7 @@ core:
pagination: pagination:
page_label: page page_label: page
size_label: items per page size_label: items per page
total_label: Total {total} items
social_auth_providers: social_auth_providers:
title: Third-party login title: Third-party login
app_download_alert: app_download_alert:

View File

@ -1149,6 +1149,7 @@ core:
pagination: pagination:
page_label: page_label:
size_label: 条 / 页 size_label: 条 / 页
total_label: 共 {total} 项数据
social_auth_providers: social_auth_providers:
title: 三方登录 title: 三方登录
app_download_alert: app_download_alert:

View File

@ -1149,6 +1149,7 @@ core:
pagination: pagination:
page_label: page_label:
size_label: 條 / 頁 size_label: 條 / 頁
total_label: 共 {total} 項資料
social_auth_providers: social_auth_providers:
title: 三方登入 title: 三方登入
app_download_alert: app_download_alert:

View File

@ -669,16 +669,17 @@ onMounted(() => {
</div> </div>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[60, 120, 200]" :size-options="[60, 120, 200]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -196,12 +196,15 @@ const isDisabled = (attachment: Attachment) => {
</div> </div>
</VCard> </VCard>
</div> </div>
<div class="mt-4 bg-white sm:flex sm:items-center sm:justify-end"> <div class="mt-4">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[60, 120, 200]" :size-options="[60, 120, 200]"
/> />

View File

@ -387,16 +387,17 @@ const handleApproveInBatch = async () => {
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[20, 30, 50, 100]" :size-options="[20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -409,16 +409,17 @@ watch(
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[20, 30, 50, 100]" :size-options="[20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -472,16 +472,17 @@ watch(selectedPageNames, (newValue) => {
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[20, 30, 50, 100]" :size-options="[20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -422,16 +422,17 @@ watch(
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[20, 30, 50, 100]" :size-options="[20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -483,16 +483,17 @@ watch(selectedPostNames, (newValue) => {
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[20, 30, 50, 100]" :size-options="[20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -243,16 +243,17 @@ onMounted(() => {
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:total="total" :total="total"
:size-options="[10, 20, 30, 50, 100]" :size-options="[10, 20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>

View File

@ -501,16 +501,17 @@ onMounted(() => {
</Transition> </Transition>
<template #footer> <template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination <VPagination
v-model:page="page" v-model:page="page"
v-model:size="size" v-model:size="size"
:total="total" :total="total"
:page-label="$t('core.components.pagination.page_label')" :page-label="$t('core.components.pagination.page_label')"
:size-label="$t('core.components.pagination.size_label')" :size-label="$t('core.components.pagination.size_label')"
:total-label="
$t('core.components.pagination.total_label', { total: total })
"
:size-options="[20, 30, 50, 100]" :size-options="[20, 30, 50, 100]"
/> />
</div>
</template> </template>
</VCard> </VCard>
</div> </div>