mirror of https://github.com/halo-dev/halo
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
parent
4733008e16
commit
02c47f552a
|
@ -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>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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]"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue