feat(model): add pagination

This commit is contained in:
Quicy
2026-01-20 14:50:11 +08:00
parent 42372ddea3
commit 4e3245f290
11 changed files with 312 additions and 18 deletions
@@ -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/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/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/button'
import { reactiveOmit } from "@vueuse/core"
import { PaginationListItem } from "reka-ui"
import { cn } from '#/lib/utils'
import { buttonVariants } from '#/components/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/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/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/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/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/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/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"
+1
View File
@@ -17,6 +17,7 @@ export * from './components/input-group/index'
export * from './components/kbd/index'
export * from './components/label/index'
export * from './components/native-select/index'
export * from './components/pagination/index'
export * from './components/radio-group/index'
export * from './components/scroll-area/index'
export * from './components/select/index'
+64 -18
View File
@@ -2,9 +2,16 @@
// import type { Payment } from '@/components/columns'
import { h, computed, ref, provide, watch } from 'vue'
import CreateModel from '@/components/CreateModel/index.vue'
import { useQuery,useMutation,useQueryCache } from '@pinia/colada'
import { useQuery, useMutation, useQueryCache } from '@pinia/colada'
import {
Button,
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationNext,
PaginationPrevious
} from '@memoh/ui'
import DataTable from '@/components/DataTable/index.vue'
import request from '@/utils/request'
@@ -12,6 +19,7 @@ import { type ColumnDef } from '@tanstack/vue-table'
interface ModelType {
apiKey: string,
baseUrl: string,
@@ -19,24 +27,24 @@ interface ModelType {
modelId: string,
name: string,
type: 'chat' | 'embedding',
id:string
id: string
}
const openDialogModel = ref(false)
const editModelInfo = ref<ModelType & {id:string} |null>(null)
const editModelInfo = ref<ModelType & { id: string } | null>(null)
provide('open', openDialogModel)
provide('editModelInfo', editModelInfo)
watch(openDialogModel, () => {
if (!openDialogModel.value) {
editModelInfo.value=null
editModelInfo.value = null
}
}, {
immediate:true
immediate: true
})
const cacheQuery=useQueryCache()
const cacheQuery = useQueryCache()
const {
mutate: deleteModel,
} = useMutation({
@@ -47,9 +55,9 @@ const {
}),
onSettled: () => {
cacheQuery.invalidateQueries({
key:['models']
})
}
key: ['models']
})
}
})
const columns: ColumnDef<ModelType>[] = [
@@ -83,16 +91,17 @@ const columns: ColumnDef<ModelType>[] = [
{
accessorKey: 'control',
header: () => h('div', { class: 'text-center' }, '操作'),
cell: ({row}) => h('div', { class: ' w-full flex justify-around' }, [h(Button, {
cell: ({ row }) => h('div', { class: ' w-full flex justify-around' }, [h(Button, {
'onClick': () => {
editModelInfo.value=row.original
openDialogModel.value = true
editModelInfo.value = row.original
openDialogModel.value = true
}
}, () => '编辑'), h(Button, {
variant: 'destructive', onClick() {
deleteModel(row.original.id)
}},()=>'删除')])
}
}, () => '删除')])
}
]
@@ -107,10 +116,12 @@ const { data: modelData } = useQuery({
const displayFormat = computed(() => {
return modelData.value?.data?.items?.map((currentModel: { model: Omit<ModelType,'id'>, id: 'string' }) => ({id:currentModel.id,...currentModel.model})) ?? []
return modelData.value?.data?.items?.map((currentModel: { model: Omit<ModelType, 'id'>, id: 'string' }) => ({ id: currentModel.id, ...currentModel.model })) ?? []
})
const pagination = computed(() => {
return modelData.value?.data.pagination ?? {}
})
</script>
@@ -118,12 +129,47 @@ const displayFormat = computed(() => {
<div class="w-full py-10 mx-auto">
<div class="flex mb-4">
<CreateModel />
</div>
</div>
<div class="[&_td:last-child]:w-45">
<DataTable
:columns="columns"
:data="displayFormat"
/>
/>
</div>
<div class="flex flex-col mt-4">
<Pagination
v-slot="{ page }"
:total="pagination.value?.total ?? 0"
:items-per-page="10"
show-edges
>
<PaginationContent v-slot="{ items }">
<PaginationPrevious />
<template
v-for="(item, index) in items"
:key="index"
>
<PaginationItem
v-if="item.type === 'page'"
:key="index"
:value="item.value"
:is-active="item.value === page"
>
{{ item.value }}
</PaginationItem>
<PaginationEllipsis
v-else
:key="item.type"
:index="index"
class="w-9 h-9 flex items-center justify-center"
>
&#8230;
</PaginationEllipsis>
</template>
<PaginationNext />
</PaginationContent>
</Pagination>
</div>
</div>
</template>