# 分页

## 使用方法

`Pagination` 是一个分页导航组件，用于将大量数据分割成多个页面展示。用户可以通过点击页码、上下页按钮或输入页码跳转到指定页面。

基本用法需要提供总条目数和当前页码：

```vue
<script setup lang="ts">
import { Pagination } from '@astryx-vue/core/Pagination'
import { ref } from 'vue'

const page = ref(1)
const total = ref(100)
</script>

<template>
  <Pagination v-model:page="page" :total="total" />
</template>
```

通过 `pageSize` 属性设置每页显示的条目数。开启 `showSizeChanger` 后，用户可以自行选择每页显示数量。按钮和总数文案默认是英文，调用 `provideAstryxLocale('zh-CN')` 后改为中文。

### 结构

- **分页**: 装着当前分页控件和可选的每页条数选择器的导航地标。
- **每页条数**: 可选的 Selector，用来选择每页显示多少项。
- **首页和末页按钮**: 可选的 Button，跳到已知的第一页或最后一页。
- **上一页和下一页**: 在页面之间向前或向后移动的按钮。
- **页码按钮**: 编号呈现方式里，直接选中某一页的按钮。
- **省略号**: 不相邻的页码按钮之间的省略标记。
- **计数读出**: 显示当前条目范围和总条目数的文字。
- **紧凑读出**: 显示当前页和总页数的文字。
- **圆点**: 反映并切换当前页的页指示。
- **页码输入标签**: 可编辑页码字段前面的可见标签。
- **页码输入**: 用来直接输入页码的 NumberInput。
- **页码总数**: 尾部文字，显示已知的总页数。

## 最佳实践

| 指引 | 实践 |
| --- | --- |
| 推荐 | 在表格、列表等数据展示场景使用分页控制。 |
| 推荐 | 数据量大时提供页码跳转和每页数量选择功能。 |
| 推荐 | 显示总条目数和当前范围，让用户了解数据规模。 |
| 避免 | 在窄屏上铺一长排页码按钮。页码很多时让控件保持紧凑。 |

## 示例

### 圆点轮播

圆点分页用来翻过几条评价。总数不大时，位置比页码更重要。

```vue
<script setup lang="ts">
import { Avatar } from '@astryx-vue/core/Avatar'
import { Card } from '@astryx-vue/core/Card'
import { Icon } from '@astryx-vue/core/Icon'
import { Pagination } from '@astryx-vue/core/Pagination'
import { HStack, VStack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { ref } from 'vue'

const reviews = [
  {
    name: 'Jeannie Grant',
    date: 'June 01, 2025',
    stars: 5,
    quote: 'A thorough report was done on our financial situation. Better deals were found and processed on our behalf, which took a lot of stress away.',
  },
  {
    name: 'Derval Russell',
    date: 'November 09, 2025',
    stars: 5,
    quote: 'I have been a client for 8 years now and have always found the advice provided excellent. They take the time to explain things clearly.',
  },
  {
    name: 'Claire Dawson',
    date: 'October 15, 2025',
    stars: 5,
    quote: 'Constantly professional and concise. Our mortgage process was smooth from start to finish thanks to their dedicated team.',
  },
  {
    name: 'Marcus Webb',
    date: 'September 22, 2025',
    stars: 4,
    quote: 'Great service overall. The team was responsive and knowledgeable. Would definitely recommend to anyone looking for solid financial advice.',
  },
]

const page = ref(1)
</script>

<template>
  <VStack :gap="3" style="width: 100%; max-width: 480px">
    <Card :padding="5">
      <VStack :gap="3">
        <HStack :gap="0">
          <Icon
            v-for="star in reviews[page - 1].stars"
            :key="star"
            color="warning"
            icon="i-carbon-star-filled"
            size="sm"
          />
        </HStack>
        <Text type="body">
          {{ reviews[page - 1].quote }}
        </Text>
        <HStack align="center" :gap="3" h-align="start">
          <Avatar :name="reviews[page - 1].name" size="md" />
          <VStack :gap="0">
            <Text type="supporting" weight="bold">
              {{ reviews[page - 1].name }}
            </Text>
            <Text color="secondary" type="supporting">
              {{ reviews[page - 1].date }}
            </Text>
          </VStack>
        </HStack>
      </VStack>
    </Card>
    <Pagination
      v-model:page="page"
      :total-pages="reviews.length"
      variant="dots"
      style="justify-content: center; padding-top: 4px"
    />
  </VStack>
</template>
```

### 每页数量

分页放在交易表格下面，并可以调整每页行数。

```vue
<script setup lang="ts">
import { Heading } from '@astryx-vue/core/Heading'
import { Pagination } from '@astryx-vue/core/Pagination'
import { VStack } from '@astryx-vue/core/Stack'
import { Table } from '@astryx-vue/core/Table'
import { ref } from 'vue'

const rows = [
  { id: '1', date: 'Apr 18', description: 'Payment received', amount: '$2,450.00' },
  { id: '2', date: 'Apr 15', description: 'Subscription renewal', amount: '$99.00' },
  { id: '3', date: 'Apr 12', description: 'Refund issued', amount: '-$180.00' },
  { id: '4', date: 'Apr 10', description: 'Invoice paid', amount: '$1,200.00' },
]

const columns = [
  { key: 'date', header: 'Date' },
  { key: 'description', header: 'Description' },
  { key: 'amount', header: 'Amount' },
]

const page = ref(1)
const pageSize = ref(10)
</script>

<template>
  <VStack style="width: 100%; max-width: 500px">
    <Heading :level="4">
      Transactions
    </Heading>
    <Table :columns="columns" :data="rows" id-key="id" />
    <Pagination
      v-model:page="page"
      v-model:page-size="pageSize"
      show-size-changer
      :page-size-options="[10, 25, 50, 100]"
      :total="350"
      variant="count"
      style="padding-top: 8px; flex-direction: row-reverse"
    />
  </VStack>
</template>
```

### 表格分页

小号的计数分页放在表格下面，显示当前页覆盖的条目范围。

```vue
<script setup lang="ts">
import { Pagination } from '@astryx-vue/core/Pagination'
import { VStack } from '@astryx-vue/core/Stack'
import { Table } from '@astryx-vue/core/Table'
import { computed, ref } from 'vue'

const allRows = [
  { id: '1', name: 'Olivia Chen', role: 'Engineer', status: 'Active' },
  { id: '2', name: 'Marcus Rivera', role: 'Designer', status: 'Active' },
  { id: '3', name: 'Aisha Patel', role: 'Marketing', status: 'Invited' },
  { id: '4', name: 'James Okafor', role: 'Engineer', status: 'Active' },
  { id: '5', name: 'Sofia Nguyen', role: 'Sales', status: 'Active' },
  { id: '6', name: 'Liam Johansson', role: 'Engineer', status: 'Inactive' },
  { id: '7', name: 'Elena Kowalski', role: 'Designer', status: 'Active' },
  { id: '8', name: 'David Kim', role: 'Marketing', status: 'Active' },
  { id: '9', name: 'Priya Sharma', role: 'Sales', status: 'Invited' },
  { id: '10', name: 'Noah Tanaka', role: 'Engineer', status: 'Active' },
  { id: '11', name: 'Fatima Al-Rashid', role: 'Designer', status: 'Active' },
  { id: '12', name: 'Carlos Mendez', role: 'Marketing', status: 'Inactive' },
]

const columns = [
  { key: 'name', header: 'Name' },
  { key: 'role', header: 'Role' },
  { key: 'status', header: 'Status' },
]

const pageSize = 4
const page = ref(1)
const pageRows = computed(() => allRows.slice((page.value - 1) * pageSize, page.value * pageSize))
</script>

<template>
  <VStack style="width: 100%; max-width: 500px">
    <Table :columns="columns" :data="pageRows" id-key="id" />
    <Pagination
      v-model:page="page"
      :page-size="pageSize"
      size="sm"
      :total="allRows.length"
      variant="count"
      style="justify-content: center; padding-top: 8px"
    />
  </VStack>
</template>
```

### 输入页码

输入页码后跳转，两侧可以直达首页和末页。总数未知时改用 hasMore，不再显示末页。

```vue
<script setup lang="ts">
import { Pagination } from '@astryx-vue/core/Pagination'
import { ref } from 'vue'

const page = ref(3)
</script>

<template>
  <Pagination
    v-model:page="page"
    :page-size="10"
    :total="240"
    variant="input"
  />
</template>
```

## Pagination Props

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `page` | `number` | `1` | 当前页码，从 1 开始。 |
| `pageSize` | `number` | `10` | 每页条目数。 |
| `total` | `number` | `0` | 总条目数。 |
| `totalItems` | `number` | — | 总条目数，`total` 的原版名字。同时设置时优先于 `total` 和 `totalPages`。 |
| `label` | `string` | `Pagination` / `分页` | 导航区的无障碍名称。 |
| `showSizeChanger` | `boolean` | `false` | 是否显示每页数量选择器。 |
| `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | 每页数量选项。 |
| `showTotal` | `boolean` | `false` | 是否显示总条目数。 |
| `isDisabled` | `boolean` | `false` | 是否禁用。 |
| `disabled` | `boolean` | `false` | `isDisabled` 的旧名字，任一为真即禁用。 |
| `prevLabel` | `string` | `Previous` / `上一页` | 上一页按钮文字。未设置语言时为英文，提供 `zh-CN` 后为中文。 |
| `nextLabel` | `string` | `Next` / `下一页` | 下一页按钮文字。未设置语言时为英文，提供 `zh-CN` 后为中文。 |
| `pageSizeLabel` | `string` | `Per page` / `每页` | 每页数量选择器的标签。未设置语言时为英文，提供 `zh-CN` 后为中文。 |
| `variant` | `'pages' \| 'count' \| 'compact' \| 'dots' \| 'input' \| 'none'` | `'pages'` | 中间区域。 `input` 是可输入的页码。 |
| `siblingCount` | `number` | `1` | 页码形态下，当前页两侧各留几个页码。窄容器会再收一档。 |
| `step` | `number` | `1` | 上一页和下一页每次跳几页。 |
| `hasMore` | `boolean` | — | 总数未知时，后面是否还有内容。`total` 大于 0 时以总条目数为准。 |
| `pageLabel` | `string` | `Page` / `页` | `input` 形态里，输入框前面的名词。 |
| `hasFirstLast` | `boolean` | 仅 `input` 且总页数已知 | 是否在上一页和下一页外侧放跳到首尾的按钮。 |
| `changeAction` | `(page: number) => void \| Promise<void>` | — | 页码提交后再跑的动作。结束前控件标成忙碌，失败也不会把页码改回去。 |

## Pagination 事件

| 名称 | 载荷 | 说明 |
| --- | --- | --- |
| `update:page` | `page: number` | 页码变化时触发。 |
| `update:pageSize` | `pageSize: number` | 每页数量变化时触发。 |
| `pageSizeChange` | `pageSize: number` | 每页数量变化时触发，在页码回到第一页之前发出。 |
| `change` | `page: number, pageSize: number` | 页码或每页数量变化时触发。 |

## Pagination 插槽

| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `total` | `{ total, from, to }` | 自定义总数显示。 |
