chore: update api Pagination css style
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB |
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "my-vue-app",
|
||||
"name": "message-nest-web",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "my-vue-app",
|
||||
"name": "message-nest-web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite",
|
||||
"demo": "vite --mode demo",
|
||||
"build:demo": "vite build --mode demo",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
|
||||
@@ -160,8 +160,3 @@ export default {
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.setting-container {
|
||||
max-width: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +1,65 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm text-gray-500">
|
||||
共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Button size="sm" variant="outline" :disabled="currentPage <= 1" @click="$emit('page-change', currentPage - 1)">
|
||||
上一页
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" :disabled="currentPage >= totalPages"
|
||||
@click="$emit('page-change', currentPage + 1)">
|
||||
下一页
|
||||
</Button>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<Pagination
|
||||
:total="total"
|
||||
:items-per-page="pageSize"
|
||||
:sibling-count="1"
|
||||
:show-edges="true"
|
||||
:default-page="currentPage"
|
||||
:page="currentPage"
|
||||
@update:page="handlePageChange"
|
||||
class="justify-end"
|
||||
>
|
||||
<PaginationContent>
|
||||
<PaginationItem :value="currentPage - 1">
|
||||
<PaginationPrevious @click="handlePrevious" :disabled="currentPage <= 1" class="mr-3">
|
||||
上一页
|
||||
</PaginationPrevious>
|
||||
</PaginationItem>
|
||||
|
||||
<template v-for="(page, index) in items" :key="index">
|
||||
<PaginationItem v-if="page.type === 'page'" :value="page.value" :is-active="page.value === currentPage">
|
||||
<button
|
||||
:class="[
|
||||
'h-9 w-9 rounded-md border transition-colors',
|
||||
page.value === currentPage
|
||||
? 'bg-primary text-primary-foreground border-primary'
|
||||
: 'border-input bg-background hover:bg-accent hover:text-accent-foreground'
|
||||
]"
|
||||
@click="handlePageChange(page.value)"
|
||||
>
|
||||
{{ page.value }}
|
||||
</button>
|
||||
</PaginationItem>
|
||||
<PaginationEllipsis v-else-if="page.type === 'ellipsis'" :index="index" />
|
||||
</template>
|
||||
|
||||
<PaginationItem :value="currentPage + 1">
|
||||
<PaginationNext @click="handleNext" :disabled="currentPage >= totalPages" class="ml-3">
|
||||
下一页
|
||||
</PaginationNext>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from '@/components/ui/pagination'
|
||||
|
||||
interface Props {
|
||||
total: number
|
||||
@@ -27,13 +69,78 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
// const _emit = defineEmits<{
|
||||
// 'page-change': [page: number]
|
||||
// }>()
|
||||
const emit = defineEmits<{
|
||||
'page-change': [page: number]
|
||||
}>()
|
||||
|
||||
const totalPages = computed(() => {
|
||||
return Math.ceil(props.total / props.pageSize)
|
||||
})
|
||||
|
||||
// 分页项目类型
|
||||
type PaginationItem =
|
||||
| { type: 'page'; value: number }
|
||||
| { type: 'ellipsis' }
|
||||
|
||||
// 生成分页项目
|
||||
const items = computed((): PaginationItem[] => {
|
||||
const pages: PaginationItem[] = []
|
||||
const total = totalPages.value
|
||||
const current = props.currentPage
|
||||
|
||||
if (total <= 7) {
|
||||
// 如果总页数小于等于7,显示所有页码
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pages.push({ type: 'page', value: i })
|
||||
}
|
||||
} else {
|
||||
// 复杂分页逻辑
|
||||
if (current <= 2) {
|
||||
// 当前页在前面
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
pages.push({ type: 'page', value: i })
|
||||
}
|
||||
pages.push({ type: 'ellipsis' })
|
||||
pages.push({ type: 'page', value: total })
|
||||
} else if (current >= total - 3) {
|
||||
// 当前页在后面
|
||||
pages.push({ type: 'page', value: 1 })
|
||||
pages.push({ type: 'ellipsis' })
|
||||
for (let i = total - 4; i <= total; i++) {
|
||||
pages.push({ type: 'page', value: i })
|
||||
}
|
||||
} else {
|
||||
// 当前页在中间
|
||||
pages.push({ type: 'page', value: 1 })
|
||||
pages.push({ type: 'ellipsis' })
|
||||
for (let i = current - 1; i <= current + 1; i++) {
|
||||
pages.push({ type: 'page', value: i })
|
||||
}
|
||||
pages.push({ type: 'ellipsis' })
|
||||
pages.push({ type: 'page', value: total })
|
||||
}
|
||||
}
|
||||
|
||||
return pages
|
||||
})
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
if (page !== props.currentPage && page >= 1 && page <= totalPages.value) {
|
||||
emit('page-change', page)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePrevious = () => {
|
||||
if (props.currentPage > 1) {
|
||||
emit('page-change', props.currentPage - 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
if (props.currentPage < totalPages.value) {
|
||||
emit('page-change', props.currentPage + 1)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -4,20 +4,20 @@ import { cva } from "class-variance-authority"
|
||||
export { default as Button } from "./Button.vue"
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive active:scale-95 active:transition-transform active:duration-75",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 active:bg-primary/80 hover:shadow-sm",
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 active:bg-destructive/80 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 hover:shadow-sm",
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:bg-input/30 dark:border-input dark:hover:bg-input/50 hover:shadow-sm",
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80 active:bg-secondary/70 hover:shadow-sm",
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:hover:bg-accent/50",
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationRootEmits, PaginationRootProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { PaginationRoot, useForwardPropsEmits } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<PaginationRootProps & {
|
||||
class?: HTMLAttributes["class"]
|
||||
}>()
|
||||
const emits = defineEmits<PaginationRootEmits>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationRoot
|
||||
v-slot="slotProps"
|
||||
data-slot="pagination"
|
||||
v-bind="forwarded"
|
||||
:class="cn('mx-auto flex w-full justify-center', props.class)"
|
||||
>
|
||||
<slot v-bind="slotProps" />
|
||||
</PaginationRoot>
|
||||
</template>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationListProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { PaginationList } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<PaginationListProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationList
|
||||
v-slot="slotProps"
|
||||
data-slot="pagination-content"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('flex flex-row items-center gap-1', props.class)"
|
||||
>
|
||||
<slot v-bind="slotProps" />
|
||||
</PaginationList>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationEllipsisProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { MoreHorizontal } from "lucide-vue-next"
|
||||
import { PaginationEllipsis } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<PaginationEllipsisProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationEllipsis
|
||||
data-slot="pagination-ellipsis"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('flex size-9 items-center justify-center', props.class)"
|
||||
>
|
||||
<slot>
|
||||
<MoreHorizontal class="size-4" />
|
||||
<span class="sr-only">More pages</span>
|
||||
</slot>
|
||||
</PaginationEllipsis>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationFirstProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import type { ButtonVariants } from '@/components/ui/button'
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { ChevronLeftIcon } from "lucide-vue-next"
|
||||
import { PaginationFirst, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<PaginationFirstProps & {
|
||||
size?: ButtonVariants["size"]
|
||||
class?: HTMLAttributes["class"]
|
||||
}>(), {
|
||||
size: "default",
|
||||
})
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "size")
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationFirst
|
||||
data-slot="pagination-first"
|
||||
:class="cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot>
|
||||
<ChevronLeftIcon />
|
||||
<span class="hidden sm:block">First</span>
|
||||
</slot>
|
||||
</PaginationFirst>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationListItemProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import type { ButtonVariants } from '@/components/ui/button'
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { PaginationListItem } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<PaginationListItemProps & {
|
||||
size?: ButtonVariants["size"]
|
||||
class?: HTMLAttributes["class"]
|
||||
isActive?: boolean
|
||||
}>(), {
|
||||
size: "icon",
|
||||
})
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "size", "isActive")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationListItem
|
||||
data-slot="pagination-item"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? 'outline' : 'ghost',
|
||||
size,
|
||||
}),
|
||||
props.class)"
|
||||
>
|
||||
<slot />
|
||||
</PaginationListItem>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationLastProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import type { ButtonVariants } from '@/components/ui/button'
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { ChevronRightIcon } from "lucide-vue-next"
|
||||
import { PaginationLast, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<PaginationLastProps & {
|
||||
size?: ButtonVariants["size"]
|
||||
class?: HTMLAttributes["class"]
|
||||
}>(), {
|
||||
size: "default",
|
||||
})
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "size")
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationLast
|
||||
data-slot="pagination-last"
|
||||
:class="cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot>
|
||||
<span class="hidden sm:block">Last</span>
|
||||
<ChevronRightIcon />
|
||||
</slot>
|
||||
</PaginationLast>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationNextProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import type { ButtonVariants } from '@/components/ui/button'
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { ChevronRightIcon } from "lucide-vue-next"
|
||||
import { PaginationNext, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<PaginationNextProps & {
|
||||
size?: ButtonVariants["size"]
|
||||
class?: HTMLAttributes["class"]
|
||||
}>(), {
|
||||
size: "default",
|
||||
})
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "size")
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationNext
|
||||
data-slot="pagination-next"
|
||||
:class="cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot>
|
||||
<span class="hidden sm:block">Next</span>
|
||||
<ChevronRightIcon />
|
||||
</slot>
|
||||
</PaginationNext>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationPrevProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import type { ButtonVariants } from '@/components/ui/button'
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { ChevronLeftIcon } from "lucide-vue-next"
|
||||
import { PaginationPrev, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = withDefaults(defineProps<PaginationPrevProps & {
|
||||
size?: ButtonVariants["size"]
|
||||
class?: HTMLAttributes["class"]
|
||||
}>(), {
|
||||
size: "default",
|
||||
})
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "size")
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PaginationPrev
|
||||
data-slot="pagination-previous"
|
||||
:class="cn(buttonVariants({ variant: 'ghost', size }), 'gap-1 px-2.5 sm:pr-2.5', props.class)"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot>
|
||||
<ChevronLeftIcon />
|
||||
<span class="hidden sm:block">Previous</span>
|
||||
</slot>
|
||||
</PaginationPrev>
|
||||
</template>
|
||||
@@ -0,0 +1,8 @@
|
||||
export { default as Pagination } from "./Pagination.vue"
|
||||
export { default as PaginationContent } from "./PaginationContent.vue"
|
||||
export { default as PaginationEllipsis } from "./PaginationEllipsis.vue"
|
||||
export { default as PaginationFirst } from "./PaginationFirst.vue"
|
||||
export { default as PaginationItem } from "./PaginationItem.vue"
|
||||
export { default as PaginationLast } from "./PaginationLast.vue"
|
||||
export { default as PaginationNext } from "./PaginationNext.vue"
|
||||
export { default as PaginationPrevious } from "./PaginationPrevious.vue"
|
||||
Reference in New Issue
Block a user