fix(frontend): prevent pagination overflow on narrow screens

Adapt visible page buttons to the pagination container width while preserving the existing button styles. Hide the total count on narrow containers and keep the page-size selector on the left with navigation on the right. Retain the total count and full pager on wider layouts.

Validation: browser layout checks across 320–1280px widths and up to 1380 pages, pagination interactions and disabled states, frontend typecheck, targeted ESLint, and diff whitespace checks passed.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-09-09 19:26:55 +08:00
co-authored by Codex
parent a2fbc073a4
commit 70ff923033
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { BaseTablePagination } from './pagination'
import type { BaseTablePagination, PagerItem } from './pagination'
import { ChevronLeft, ChevronRight } from '@lucide/vue'
import { computed } from 'vue'
@@ -26,6 +26,29 @@ const totalPages = computed(() => getTotalPages(props.pagination))
const currentPage = computed(() => getCurrentPage(props.pagination, totalPages.value))
const pageSizeOptions = computed(() => getPageSizeOptions(props.pagination))
const pagerItems = computed(() => getPagerItems(totalPages.value, currentPage.value))
const adjacentPagerItems = computed(() => {
const count = Math.min(3, totalPages.value)
const start = Math.max(1, Math.min(currentPage.value - 1, totalPages.value - count + 1))
return Array.from({ length: count }, (_, index) => start + index)
})
const compactPagerItems = computed<PagerItem[]>(() => {
const total = totalPages.value
const current = currentPage.value
if (total <= 5)
return Array.from({ length: total }, (_, index) => index + 1)
if (current <= 3)
return [1, 2, 3, 'ellipsis', total]
if (current >= total - 2)
return [1, 'ellipsis', total - 2, total - 1, total]
return [1, 'ellipsis', current, 'ellipsis', total]
})
const pagerVisibilityClasses = [
'flex @min-[19.5rem]/pagination:hidden',
'hidden @min-[19.5rem]/pagination:flex @min-[24.5rem]/pagination:hidden',
'hidden @min-[24.5rem]/pagination:flex @min-[29.5rem]/pagination:hidden',
'hidden @min-[29.5rem]/pagination:flex',
]
const pageSizeModel = computed({
get: () => String(props.pagination.pageSize),
@@ -61,25 +84,25 @@ function paginationPageClass(page: number) {
<template>
<footer
class="mt-2 flex min-h-10 shrink-0 flex-wrap items-center justify-between gap-3 px-0 py-1"
class="@container/pagination mt-2 min-h-10 min-w-0 shrink-0 px-0 py-1"
>
<div
class="flex min-w-0 items-center gap-2.5 text-cp-sm font-emphasis text-cp-text-secondary"
>
<span class="whitespace-nowrap">共 {{ pagination.total }} 条</span>
</div>
<div class="flex items-center gap-2">
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-2 @min-[36rem]/pagination:grid-cols-[minmax(0,1fr)_auto_auto] @min-[36rem]/pagination:gap-3">
<span class="hidden min-w-0 truncate text-cp-sm font-emphasis text-cp-text-secondary @min-[36rem]/pagination:block">
共 {{ pagination.total }} 条
</span>
<BaseSelect
v-model="pageSizeModel"
aria-label="每页条数"
:options="pageSizeOptions"
:disabled="loading"
size="sm"
class="w-28"
class="w-28 shrink-0"
/>
<div class="flex items-center gap-2">
<nav
aria-label="分页"
class="flex shrink-0 items-center justify-self-end gap-2"
>
<BaseIconButton
variant="secondary"
size="sm"
@@ -90,24 +113,31 @@ function paginationPageClass(page: number) {
<ChevronLeft class="size-4" />
</BaseIconButton>
<template v-for="(item, index) in pagerItems" :key="`${item}-${index}`">
<span
v-if="item === 'ellipsis'"
class="inline-flex size-8 items-center justify-center text-xs font-bold text-cp-text-quaternary"
>
…
</span>
<button
v-else
type="button"
:class="paginationPageClass(item)"
:disabled="loading || item === currentPage"
:aria-current="item === currentPage ? 'page' : undefined"
@click="goToPage(item)"
>
{{ item }}
</button>
</template>
<div
v-for="(items, variant) in [[currentPage], adjacentPagerItems, compactPagerItems, pagerItems]"
:key="variant"
class="items-center gap-2"
:class="pagerVisibilityClasses[variant]"
>
<template v-for="(item, index) in items" :key="`${item}-${index}`">
<span
v-if="item === 'ellipsis'"
class="inline-flex size-8 items-center justify-center text-xs font-bold text-cp-text-quaternary"
>
…
</span>
<button
v-else
type="button"
:class="paginationPageClass(item)"
:disabled="loading || item === currentPage"
:aria-current="item === currentPage ? 'page' : undefined"
@click="goToPage(item)"
>
{{ item }}
</button>
</template>
</div>
<BaseIconButton
variant="secondary"
@@ -118,7 +148,7 @@ function paginationPageClass(page: number) {
>
<ChevronRight class="size-4" />
</BaseIconButton>
</div>
</nav>
</div>
</footer>
</template>