SwPagination
SwPagination is a numbered pager: a controlled v-model:page; when the page changes, the parent replaces the whole list data set (replace-mode) — this and SwGallery's @load-more (append-mode) are two mutually exclusive data flows. Pick one per scenario; don't mix them.
Responsiveness is a pure CSS media-query dual layout: the full desktop version (page-number window + ellipsis + jumper + total) and the simplified mobile version (prev N / M next) are both rendered and toggled by breakpoint (768px, aligned with the framework) — no JS width measuring, no SSR hydration risk.
Basic pagination (page count from total/pageSize + total count text)
Combining with SwGallery (numbered-paging scenario)
Grid + pager = a SwGallery with loadMode="none" + this component; replace the whole items set whenever the page changes:
SwGallery (none) + SwPagination combined paging
<script setup lang="ts">
const page = ref(1)
const { data } = await useAsyncData(
() => `list-${page.value}`,
() => request<MovieList>('/movie/list', { body: { page: page.value, page_size: 24 } }),
{ watch: [page] },
)
</script>
<template>
<SwGallery :items="data?.list ?? []" load-mode="none">
<template #default="{ item }">…</template>
</SwGallery>
<!-- legacy protocol sends numbers as strings: normalize with a one-line unary + -->
<SwPagination v-model:page="page" :page-count="+(data?.last_page ?? 1)" :total="+(data?.total ?? 0)" show-total />
</template>
Customization
pageCount passed directly + window 7 + custom arrows + disabled loading lock
maxVisible window folding + showTotal
disabled loading lock
Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
page (v-model) | number | ✓ | — | Current page number (1-based, controlled) |
total | number | 0 | Total item count; combined with pageSize to derive the page count | |
pageSize | number | 20 | Items per page | |
pageCount | number | undefined | Page count passed directly (backend gives last_page); takes priority over the total-based calculation | |
maxVisible | number | 5 | Page-number window width (odd number recommended); overflow collapses into an ellipsis | |
showJumper | boolean | true | Desktop "前往 N 页" (go to page N) jumper input (submit on Enter, out-of-range values are clamped) | |
showTotal | boolean | false | "共 N 条" (N items in total) text (requires total) | |
disabled | boolean | false | Disable all interaction (while loading) | |
ariaLabel | string | '分页' | aria-label of the <nav> (Chinese for "pagination") |
Events
| Name | Payload | Description |
|---|---|---|
update:page | page: number | Page changed (not emitted for same page / out-of-range / disabled) |
Slots
| Name | Description |
|---|---|
prev / next | Custom previous / next content (defaults to inline SVG arrows) |
Style Tokens
These CSS variables can be overridden on your side (the fallback is the default):
| Variable | Default | Description |
|---|---|---|
--sw-pagination-gap | 0.25rem | Gap between page items |
--sw-pagination-item-size | 2rem | Minimum width/height of one page item (square hit area) |
--sw-pagination-item-padding | 0.375rem | Page item padding (widens items as the digit count grows) |
--sw-pagination-font-size | 0.875rem | Page item font size |
Caveats
- Hidden entirely when the page count ≤ 1 (nothing to page through, so nothing renders).
- Replace-mode vs append-mode: for infinite scroll / load more use SwGallery's loadMode; don't simulate it with page numbers.
- On ISR pages, carry the page number in the URL (a RESTful path like
/list/page/2or a GET query) so the cache key distinguishes pages — just bindv-model:pageto the route param.