Skip to content

Commit

Permalink
add pagenation
Browse files Browse the repository at this point in the history
  • Loading branch information
gusibi committed Oct 3, 2024
1 parent 19324e3 commit f2deeb4
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 72 deletions.
65 changes: 65 additions & 0 deletions components/Pagination.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<template>
<div class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6">
<div class="flex flex-1 justify-between sm:hidden">
<a href="#" @click.prevent="onPageChange(currentPage - 1)" :class="[currentPage === 1 ? 'cursor-not-allowed opacity-50' : 'hover:bg-gray-50', 'relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700']" :aria-disabled="currentPage === 1"> Previous </a>
<a href="#" @click.prevent="onPageChange(currentPage + 1)" :class="[currentPage === totalPages ? 'cursor-not-allowed opacity-50' : 'hover:bg-gray-50', 'relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700']" :aria-disabled="currentPage === totalPages"> Next </a>
</div>
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
<div>
<p class="text-sm text-gray-700"> Showing <span class="font-medium">{{ startItem }}</span> to <span class="font-medium">{{ endItem }}</span> of <span class="font-medium">{{ totalItems }}</span> results </p>
</div>
<div>
<nav class="isolate inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination">
<a href="#" @click.prevent="onPageChange(currentPage - 1)" :class="[currentPage === 1 ? 'cursor-not-allowed opacity-50' : 'hover:bg-gray-50', 'relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300']" :aria-disabled="currentPage === 1">
<span class="sr-only">Previous</span>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" />
</svg>
</a>
<template v-for="page in displayedPages" :key="page">
<a href="#" @click.prevent="onPageChange(page)" :class="[page === currentPage ? 'z-10 bg-indigo-600 text-white' : 'text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50', 'relative inline-flex items-center px-4 py-2 text-sm font-semibold focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600']">
{{ page }}
</a>
</template>
<a href="#" @click.prevent="onPageChange(currentPage + 1)" :class="[currentPage === totalPages ? 'cursor-not-allowed opacity-50' : 'hover:bg-gray-50', 'relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300']" :aria-disabled="currentPage === totalPages">
<span class="sr-only">Next</span>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
</svg>
</a>
</nav>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps({
currentPage: { type: Number, required: true },
totalItems: { type: Number, required: true },
perPage: { type: Number, required: true },
});
const emit = defineEmits(['pageChange']);
const totalPages = computed(() => Math.ceil(props.totalItems / props.perPage));
const startItem = computed(() => ((props.currentPage - 1) * props.perPage) + 1);
const endItem = computed(() => Math.min(props.currentPage * props.perPage, props.totalItems));
const displayedPages = computed(() => {
const range = 2;
const pages = [];
for (let i = Math.max(1, props.currentPage - range); i <= Math.min(totalPages.value, props.currentPage + range); i++) {
pages.push(i);
}
return pages;
});
const onPageChange = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
emit('pageChange', page);
}
};
</script>
38 changes: 26 additions & 12 deletions components/RepoTimeline.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
<template>
<div class="container max-w-content mx-auto px-4">
<div class="space-y-8">
<div v-for="post in blogPosts" :key="post.id" class="flex items-start">
<!-- <div class="flex-shrink-0 mr-4">
<span class="inline-flex items-center justify-center w-8 h-8 rounded-full text-white" :style="{ backgroundColor: getLabelColor(post.labels) }">
{{ getFirstLabelChar(post.labels) }}
</span>
</div> -->
<div class="flex-grow bg-card-light dark:bg-card-dark rounded-lg px-6 shadow-xl" :data-post-id="post.number">
<article v-for="post in blogPosts" class="bg-card-light dark:bg-card-dark rounded-lg shadow-xl overflow-hidden">
<div class="px-6">
<div class="pt-4 flex justify-between items-center text-sm">
<!-- 左上角标签 -->
<div class="flex items-center space-x-4">
<PostLabels :labels="post.labels" :repo-url="post.repo_url" />
</div>
<!-- 右上角反应 -->
<div class="flex items-center space-x-4">
<PostReactions :reactions="post.reactions" class="" />
</div>
Expand All @@ -22,7 +15,7 @@
<h1 v-if="!isMemePost(post.labels)" class="text-xl font-medium">
<NuxtLink :to="`${post.html_url}`" class="hover:underline">{{ post.title }}</NuxtLink>
</h1>
<div class="mt-2 text-sm prose dark:prose-invert" v-html="$md(truncatedBody(post.body))"></div>
<div class="prose dark:prose-invert max-w-none" v-html="$md(truncatedBody(post.body))" />
</div>
<!-- 底部信息栏 -->
<div class="pb-6 pt-3 flex justify-between items-center text-sm">
Expand All @@ -37,14 +30,17 @@
</div>
</div>
</div>
</div>
</article>
</div>
<Pagination :current-page="currentPage" :total-items="totalItems" :per-page="perPage" @page-change="onPageChange" />
</div>
<!-- <ShareModal :post="selectedPost" :is-open="isShareModalOpen" @close="closeShareModal" /> -->
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ShareModal from '~/components/ShareButton.vue'
import Pagination from '~/components/Pagination.vue'
interface BlogPost {
id: number
Expand All @@ -62,10 +58,28 @@ const props = defineProps({
blogPosts: {
type: Array as PropType<BlogPost[]>,
required: true,
default: () => [] // 添加默认值
default: () => []
},
currentPage: {
type: Number,
required: true
},
totalItems: {
type: Number,
required: true
},
perPage: {
type: Number,
required: true
}
})
const emit = defineEmits(['pageChange'])
const onPageChange = (page: number) => {
emit('pageChange', page)
}
const selectedPost = ref<BlogPost | null>(null)
const isShareModalOpen = ref(false)
Expand Down
26 changes: 9 additions & 17 deletions components/Timeline.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,38 @@
<template>
<div class="container max-w-content mx-auto px-4">
<div class="space-y-8">
<div v-for="post in blogPosts" :key="post.id" class="flex items-start">
<!-- <div class="flex-shrink-0 mr-4">
<span class="inline-flex items-center justify-center w-8 h-8 rounded-full text-white" :style="{ backgroundColor: getLabelColor(post.labels) }">
{{ getFirstLabelChar(post.labels) }}
</span>
</div> -->
<div class="flex-grow bg-card-light dark:bg-card-dark rounded-lg px-6 shadow-xl" :data-post-id="post.number">
<article v-for="post in blogPosts" class="bg-card-light dark:bg-card-dark rounded-lg shadow-xl overflow-hidden">
<div class="px-6">
<div class="pt-4 flex justify-between items-center text-sm">
<!-- 左上角标签 -->
<div class="flex items-center space-x-4">
<PostLabels :labels="post.labels" />
<PostLabels :labels="post.labels" :repo-url="post.repo_url" />
</div>
<!-- 右上角反应 -->
<div class="flex items-center space-x-4">
<PostReactions :reactions="post.reactions" class="" />
</div>
</div>
<div class="py-3">
<h1 v-if="!isMemePost(post.labels)" class="text-xl font-medium">
<NuxtLink :to="`/blog/${post.number}`" class="hover:underline">{{ post.title }}</NuxtLink>
<NuxtLink :to="`${post.html_url}`" class="hover:underline">{{ post.title }}</NuxtLink>
</h1>
<div class="mt-2 text-sm prose dark:prose-invert" v-html="$md(truncatedBody(post.body))"></div>
<div class="prose dark:prose-invert max-w-none" v-html="$md(truncatedBody(post.body))" />
</div>
<!-- 底部信息栏 -->
<div class="pb-6 pt-3 flex justify-between items-center text-sm">
<!-- 左下角 GitHub 链接 -->
<div class="flex items-center space-x-4">
<a class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300" :href="post.html_url" target="_blank">🔗</a>
<a class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300" :href="post.github_url" target="_blank">🔗</a>
<span class="text-gray-500 dark:text-gray-400">{{ formatDate(post.created_at, false) }}</span>
</div>
<div class="flex items-center space-x-4">
<CommentButton :post-number="post.number" :comment-count="post.comments || 0" />
<ShareButton :post="post" :card-selector="`[data-post-id='${post.number}']`" />
<CommentButton :post-number="post.number" :repo-url="post.repo_url" :comment-count="post.comments || 0" />
<ShareButton :post="post" :repo-url="post.repo_url" :card-selector="`[data-post-id='${post.number}']`" />
</div>
</div>
</div>
</div>
</article>
</div>
</div>
<!-- <ShareModal :post="selectedPost" :is-open="isShareModalOpen" @close="closeShareModal" /> -->
</template>
<script setup lang="ts">
import { ref } from 'vue'
Expand Down
24 changes: 24 additions & 0 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
</div>
</footer>
<div ref="floatingTimeLabel" class="fixed top-5 right-5 bg-black bg-opacity-70 text-white px-4 py-2 rounded-full text-sm opacity-0 transition-opacity duration-300"></div>
<button @click="scrollToTop" class="fixed bottom-8 right-8 p-2 rounded-full bg-indigo-600 bg-opacity-50 text-white hover:bg-opacity-75 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-opacity duration-300" :class="{ 'opacity-0': !showScrollTop, 'opacity-100': showScrollTop }">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
</svg>
</button>
</div>
</template>
<script setup lang="ts">
Expand Down Expand Up @@ -87,7 +92,26 @@ const addScrollEventListener = () => {
}, 1500)
})
}
const showScrollTop = ref(false);
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
};
const checkScroll = () => {
showScrollTop.value = window.pageYOffset > 300;
};
onMounted(() => {
window.addEventListener('scroll', checkScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', checkScroll);
});
onMounted(() => {
initializeNightMode()
addScrollEventListener()
Expand Down
2 changes: 2 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ export default defineNuxtConfig({
public: {
apiBaseUrl: process.env.API_BASE_URL || 'https://path-memo-api.gusibi.mobi',
siteUrl: process.env.SITE_URL || 'https://momo.gusibi.mobi',
repoOwner: process.env.REPO_OWNER || 'gusibi',
repoName: process.env.REPO_NAME || 'path-meme-db',
perPageSize: process.env.PER_PAGE_SIZE || '10',
},
private: {
githubToken: process.env.GITHUB_TOKEN
Expand Down
31 changes: 25 additions & 6 deletions pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
<template>
<div>
<Timeline :blogPosts="blogPosts" />
<RepoTimeline :blogPosts="blogPosts" :current-page="currentPage" :total-items="totalItems" :per-page="perPage" @page-change="onPageChange" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, watch } from 'vue'
import { useBannerContent } from '~/composables/useBannerContent'
const config = useRuntimeConfig()
const blogPosts = ref([])
const currentPage = ref(1)
const totalItems = ref(0)
const perPage = ref(parseInt(config.public.perPageSize) || 20)
const { setBannerContent } = useBannerContent()
const fetchBlogPosts = async (page = 1) => {
const { data: fetchedData } = await useAsyncData('blogPosts', () =>
$fetch(`/api/repo/${config.public.repoOwner}/${config.public.repoName}/blog-posts`, {
params: { page, perPage: perPage.value }
})
)
// 使用 useAsyncData 来获取数据
const { data: fetchedBlogPosts } = await useAsyncData('blogPosts', () => $fetch('/api/blog-posts'))
blogPosts.value = fetchedData.value?.blogPosts || []
totalItems.value = fetchedData.value?.pagination.totalItems || 0
perPage.value = fetchedData.value?.pagination.perPage || 20
currentPage.value = fetchedData.value?.pagination.currentPage || 1
}
// 当数据获取完成后,更新 blogPosts
blogPosts.value = fetchedBlogPosts.value || []
// Fetch initial data
await fetchBlogPosts()
// console.log(totalItems.value, perPage.value, currentPage.value)
// Handle page changes
const onPageChange = async (page: number) => {
await fetchBlogPosts(page)
}
// 设置 banner 内容
setBannerContent('<h1 class="text-4xl font-extrabold text-center text-white mb-6">Welcome to My Blog</h1>')
</script>
40 changes: 30 additions & 10 deletions pages/repo/[repo_owner]/[repo_name]/index.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
<template>
<div>
<RepoInfo v-if="fetchedBlogPosts && fetchedBlogPosts.repo" :repo="fetchedBlogPosts.repo" :repo-owner="route.params.repo_owner" :repo-name="route.params.repo_name" />
<RepoTimeline :blogPosts="blogPosts" />
<RepoInfo v-if="repoInfo" :repo="repoInfo" :repo-owner="route.params.repo_owner" :repo-name="route.params.repo_name" />
<RepoTimeline :blogPosts="blogPosts" :current-page="currentPage" :total-items="totalItems" :per-page="perPage" @page-change="onPageChange" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, watch } from 'vue'
import { useBannerContent } from '~/composables/useBannerContent'
const config = useRuntimeConfig()
import { useRoute } from 'vue-router'
const route = useRoute()
const blogPosts = ref([])
const repoInfo = ref({})
const currentPage = ref(1)
const totalItems = ref(0)
const perPage = ref(parseInt(config.public.perPageSize) || 20)
const { setBannerContent } = useBannerContent()
console.log("path: ", `/api/repo/${route.params.repo_owner}/${route.params.repo_name}/blog-posts`)
const fetchBlogPosts = async (page = 1) => {
const { data: fetchedData } = await useAsyncData('blogPosts', () =>
$fetch(`/api/repo/${route.params.repo_owner}/${route.params.repo_name}/blog-posts`, {
params: { page, perPage: perPage.value }
})
)
repoInfo.value = fetchedData.value?.repo || {}
blogPosts.value = fetchedData.value?.blogPosts || []
totalItems.value = fetchedData.value?.pagination.totalItems || 0
perPage.value = fetchedData.value?.pagination.perPage || 20
currentPage.value = fetchedData.value?.pagination.currentPage || 1
}
// Fetch initial data
await fetchBlogPosts()
// 使用 useAsyncData 来获取数据
const { data: fetchedBlogPosts } = await useAsyncData('blogPosts', () =>
$fetch(`/api/repo/${route.params.repo_owner}/${route.params.repo_name}/blog-posts`)
)
// 当数据获取完成后,更新 blogPosts
blogPosts.value = fetchedBlogPosts.value?.blogPosts || []
console.log(totalItems.value, perPage.value, currentPage.value)
// Handle page changes
const onPageChange = async (page: number) => {
await fetchBlogPosts(page)
}
// 设置 banner 内容
setBannerContent('<h1 class="text-4xl font-extrabold text-center text-white mb-6">Welcome to My Blog</h1>')
</script>
4 changes: 2 additions & 2 deletions pages/repo/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-8 text-center">Repositories</h1>
<div class="columns-3 sm:columns-1 md:columns-2 lg:columns-3 gap-8">
<div class="columns-1 sm:columns-1 md:columns-2 lg:columns-3 gap-2">
<div v-for="repo in repos" :key="repo.id" class="break-inside-avoid mb-4">
<RepoCard :repo="repo" />
</div>
Expand All @@ -21,7 +21,7 @@ const { data: reposData } = await useAsyncData('reposData', () =>
)
// 当数据获取完成后,更新 repos
repos.value = reposData.value || []
repos.value = reposData.value.repos || []
// 设置 banner 内容
setBannerContent('<h1 class="text-4xl font-extrabold text-center text-white mb-6">My Repositories</h1>')
Expand Down
Loading

0 comments on commit f2deeb4

Please sign in to comment.