BlogList.vue 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. <script setup>
  2. import { computed, ref } from 'vue'
  3. const props = defineProps({
  4. posts: {
  5. type: Array,
  6. required: true
  7. }
  8. })
  9. // 分页配置
  10. const postsPerPage = 10
  11. const currentPage = ref(1)
  12. // 计算分页数据
  13. const paginatedPosts = computed(() => {
  14. const start = (currentPage.value - 1) * postsPerPage
  15. const end = start + postsPerPage
  16. return props.posts.slice(start, end)
  17. })
  18. const totalPages = computed(() => Math.ceil(props.posts.length / postsPerPage))
  19. // 分页导航
  20. const goToPage = (page) => {
  21. if (page >= 1 && page <= totalPages.value) {
  22. currentPage.value = page
  23. window.scrollTo({ top: 0, behavior: 'smooth' })
  24. }
  25. }
  26. const goToPrevPage = () => goToPage(currentPage.value - 1)
  27. const goToNextPage = () => goToPage(currentPage.value + 1)
  28. </script>
  29. <template>
  30. <div class="divide-y divide-gray-200 dark:divide-slate-200/5">
  31. <div v-if="posts.length === 0" class="py-12 text-center text-gray-500">
  32. No posts found.
  33. </div>
  34. <ul v-else class="divide-y divide-gray-200 dark:divide-slate-200/5">
  35. <li class="py-12" v-for="{ title, url, date, excerpt, author } of paginatedPosts">
  36. <article class="space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline">
  37. <dl class="text-base leading-6 font-medium leading-6 text-gray-500 dark:text-gray-300">
  38. <dt class="sr-only">Published on</dt>
  39. <dd class="text-base leading-6 font-medium leading-6 text-gray-500 dark:text-gray-300">
  40. <time :datetime="new Date(date.time).toISOString()">{{ date.string }}</time>
  41. </dd>
  42. </dl>
  43. <div class="space-y-5 xl:col-span-3">
  44. <div class="space-y-6">
  45. <h2 class="text-2xl leading-8 font-bold tracking-tight">
  46. <a class="text-gray-900 dark:text-white hover:text-gray-600 dark:hover:text-gray-300" :href="url">{{ title }}</a>
  47. </h2>
  48. <div v-if="author" class="flex items-center space-x-2 text-sm text-gray-500 dark:text-gray-400">
  49. <span>by</span>
  50. <span class="font-medium">{{ author }}</span>
  51. </div>
  52. <div
  53. v-if="excerpt"
  54. class="prose dark:prose-invert max-w-none text-gray-500 dark:text-gray-300"
  55. v-html="excerpt"
  56. ></div>
  57. </div>
  58. <div class="text-base leading-6 font-medium">
  59. <a class="text-blue-600 dark:text-blue-400 hover:text-gray-600 dark:hover:text-gray-300" aria-label="read more" :href="url">Read more →</a>
  60. </div>
  61. </div>
  62. </article>
  63. </li>
  64. </ul>
  65. <!-- 分页导航 -->
  66. <div v-if="totalPages > 1" class="flex justify-center items-center space-x-4 py-8">
  67. <button
  68. @click="goToPrevPage"
  69. :disabled="currentPage === 1"
  70. class="px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700"
  71. >
  72. Previous
  73. </button>
  74. <span class="text-sm text-gray-500 dark:text-gray-400">
  75. Page {{ currentPage }} of {{ totalPages }}
  76. </span>
  77. <button
  78. @click="goToNextPage"
  79. :disabled="currentPage === totalPages"
  80. class="px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700"
  81. >
  82. Next
  83. </button>
  84. </div>
  85. </div>
  86. </template>