129 lines
2.9 KiB
Vue
129 lines
2.9 KiB
Vue
<template>
|
|
<div v-if="totalPages > 1" class="flex items-center justify-center gap-2">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-ghost"
|
|
:disabled="currentPage <= 1"
|
|
@click="goToPage(1)"
|
|
>
|
|
<IconLucideChevronFirst class="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-ghost"
|
|
:disabled="currentPage <= 1"
|
|
@click="goToPage(currentPage - 1)"
|
|
>
|
|
<IconLucideChevronLeft class="w-4 h-4" />
|
|
</button>
|
|
|
|
<template v-for="page in visiblePages" :key="page">
|
|
<span v-if="page === 'ellipsis-start' || page === 'ellipsis-end'" class="px-2">...</span>
|
|
<button
|
|
v-else
|
|
type="button"
|
|
class="btn btn-sm"
|
|
:class="page === currentPage ? 'btn-primary' : 'btn-ghost'"
|
|
@click="goToPage(page)"
|
|
>
|
|
{{ page }}
|
|
</button>
|
|
</template>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-ghost"
|
|
:disabled="currentPage >= totalPages"
|
|
@click="goToPage(currentPage + 1)"
|
|
>
|
|
<IconLucideChevronRight class="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-ghost"
|
|
:disabled="currentPage >= totalPages"
|
|
@click="goToPage(totalPages)"
|
|
>
|
|
<IconLucideChevronLast class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import IconLucideChevronFirst from '~icons/lucide/chevrons-left'
|
|
import IconLucideChevronLeft from '~icons/lucide/chevron-left'
|
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
|
import IconLucideChevronLast from '~icons/lucide/chevrons-right'
|
|
|
|
const props = defineProps({
|
|
currentPage: {
|
|
type: Number,
|
|
required: true
|
|
},
|
|
totalPages: {
|
|
type: Number,
|
|
required: true
|
|
},
|
|
maxVisiblePages: {
|
|
type: Number,
|
|
default: 5
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:currentPage'])
|
|
|
|
const visiblePages = computed(() => {
|
|
const pages = []
|
|
const total = props.totalPages
|
|
const current = props.currentPage
|
|
const maxVisible = props.maxVisiblePages
|
|
|
|
if (total <= maxVisible + 2) {
|
|
for (let i = 1; i <= total; i++) {
|
|
pages.push(i)
|
|
}
|
|
return pages
|
|
}
|
|
|
|
// Always show first page
|
|
pages.push(1)
|
|
|
|
const half = Math.floor(maxVisible / 2)
|
|
let start = Math.max(2, current - half)
|
|
let end = Math.min(total - 1, current + half)
|
|
|
|
// Adjust if near start
|
|
if (current <= half + 1) {
|
|
end = maxVisible
|
|
}
|
|
// Adjust if near end
|
|
if (current >= total - half) {
|
|
start = total - maxVisible + 1
|
|
}
|
|
|
|
if (start > 2) {
|
|
pages.push('ellipsis-start')
|
|
}
|
|
|
|
for (let i = start; i <= end; i++) {
|
|
pages.push(i)
|
|
}
|
|
|
|
if (end < total - 1) {
|
|
pages.push('ellipsis-end')
|
|
}
|
|
|
|
// Always show last page
|
|
pages.push(total)
|
|
|
|
return pages
|
|
})
|
|
|
|
const goToPage = (page) => {
|
|
if (page >= 1 && page <= props.totalPages && page !== props.currentPage) {
|
|
emit('update:currentPage', page)
|
|
}
|
|
}
|
|
</script>
|