less awful frontend
This commit is contained in:
Binary file not shown.
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 135 KiB |
@ -533,27 +533,152 @@ import { getArticles } from '~/api'
|
|||||||
import type { ArticleList } from '~/api'
|
import type { ArticleList } from '~/api'
|
||||||
|
|
||||||
const articles = ref<ArticleList[]>([])
|
const articles = ref<ArticleList[]>([])
|
||||||
|
const articlesCount = ref<number>(0)
|
||||||
|
|
||||||
|
const page = ref<number>(1)
|
||||||
|
const size = ref<number>(10)
|
||||||
|
|
||||||
async function loadArticles() {
|
async function loadArticles() {
|
||||||
const { data } = await getArticles({
|
const { data } = await getArticles({
|
||||||
page: 1,
|
page: page.value,
|
||||||
size: 10,
|
size: size.value,
|
||||||
})
|
})
|
||||||
|
|
||||||
articles.value = data.articles
|
articles.value = data.articles
|
||||||
|
articlesCount.value = data.articlesCount
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchDataOnPage({ currentPage }: { currentPage: number }) {
|
||||||
|
page.value = currentPage
|
||||||
|
loadArticles()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadArticles()
|
loadArticles()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<RouterLink
|
<div inline-flex flex-col gap-4>
|
||||||
v-for="(article, i) in articles"
|
<RouterLink
|
||||||
:key="i"
|
v-for="(article, i) in articles"
|
||||||
:to="`/articles/${article.slug}`"
|
:key="i"
|
||||||
>
|
:to="`/articles/${article.slug}`"
|
||||||
<h3>{{ article.title }}</h3>
|
inline-block border-1 border-purple-500 rounded p-4
|
||||||
</RouterLink>
|
>
|
||||||
|
<h3>{{ article.title }}</h3>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div mt-4 flex justify-center>
|
||||||
|
<OffsetPagination
|
||||||
|
:page="page"
|
||||||
|
:size="size"
|
||||||
|
:total="articlesCount"
|
||||||
|
:fetch-data="fetchDataOnPage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The reusable pagination component that use `useOffsetPagination` from VueUse:
|
||||||
|
|
||||||
|
{{< highlight host="kuberocks-demo-ui" file="src/components/OffsetPagination.vue" >}}
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const props = defineProps<{
|
||||||
|
total: number
|
||||||
|
size: number
|
||||||
|
page: number
|
||||||
|
fetchData: ({ currentPage }: { currentPage: number }) => Promise<void> | void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const pagination = computed(() =>
|
||||||
|
useOffsetPagination({
|
||||||
|
total: props.total,
|
||||||
|
page: props.page,
|
||||||
|
pageSize: props.size,
|
||||||
|
onPageChange: props.fetchData,
|
||||||
|
onPageSizeChange: props.fetchData,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
function usePagesBuilder(currentPage: number, pageCount: number) {
|
||||||
|
const pages = []
|
||||||
|
const maxPages = 5
|
||||||
|
const half = Math.floor(maxPages / 2)
|
||||||
|
const start = Math.max(currentPage - half, 1)
|
||||||
|
const end = Math.min(start + maxPages, pageCount)
|
||||||
|
|
||||||
|
for (let i = start; i <= end; i++)
|
||||||
|
pages.push(i)
|
||||||
|
|
||||||
|
if (start > 1) {
|
||||||
|
pages.unshift('...')
|
||||||
|
pages.unshift(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end < pageCount) {
|
||||||
|
pages.push('...')
|
||||||
|
pages.push(pageCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
const classes
|
||||||
|
= 'flex items-center justify-center border rounded-1 text-sm font-sans text-gray-300 border-gray-500 w-8 h-8'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div flex flex-wrap gap-1>
|
||||||
|
<button
|
||||||
|
:disabled="pagination.isFirstPage.value"
|
||||||
|
:class="[
|
||||||
|
classes,
|
||||||
|
{
|
||||||
|
'opacity-50': pagination.isFirstPage.value,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@click="pagination.prev"
|
||||||
|
>
|
||||||
|
<
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-for="item in usePagesBuilder(
|
||||||
|
pagination.currentPage.value,
|
||||||
|
pagination.pageCount.value,
|
||||||
|
)"
|
||||||
|
:key="item"
|
||||||
|
:disabled="
|
||||||
|
pagination.currentPage.value === item || !Number.isInteger(item)
|
||||||
|
"
|
||||||
|
:class="[
|
||||||
|
classes,
|
||||||
|
{
|
||||||
|
'opacity-50': !Number.isInteger(item),
|
||||||
|
'text-white border-purple-500 bg-purple-500':
|
||||||
|
pagination.currentPage.value === item,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@click="pagination.currentPage.value = Number(item)"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="pagination.isLastPage.value"
|
||||||
|
:class="[
|
||||||
|
classes,
|
||||||
|
{
|
||||||
|
'opacity-50': pagination.isLastPage.value,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@click="pagination.next"
|
||||||
|
>
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -583,9 +708,13 @@ getArticle()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="article">
|
<div v-if="article">
|
||||||
<h1>{{ article.title }}</h1>
|
<h1 mb-6 text-2xl font-bold>
|
||||||
<p>{{ article.description }}</p>
|
{{ article.title }}
|
||||||
<div>{{ article.body }}</div>
|
</h1>
|
||||||
|
<p mb-4 italic>
|
||||||
|
{{ article.description }}
|
||||||
|
</p>
|
||||||
|
<div prose v-html="article.body" />
|
||||||
<div>
|
<div>
|
||||||
<button m-3 mt-8 text-sm btn @click="router.back()">
|
<button m-3 mt-8 text-sm btn @click="router.back()">
|
||||||
Back
|
Back
|
||||||
|
Reference in New Issue
Block a user