PostView.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <script setup lang="ts">
  2. import type { PostSketch } from '@/models'
  3. import { computed, onMounted, ref, watch } from 'vue'
  4. import { api } from '@/utils/axios'
  5. import PostCard from '@/components/PostCard.vue'
  6. import { useTokenStore } from '@/stores/auth.ts'
  7. import { delay } from '@/utils'
  8. import { writeIcon } from '@/assets/icons.ts'
  9. import { router } from '@/router'
  10. const data = ref<PostSketch[]>([])
  11. const pageSize = 10
  12. const isEnd = ref(false)
  13. const tokenStore = useTokenStore()
  14. function nextDatum() {
  15. if (isEnd.value) {
  16. return
  17. }
  18. api
  19. .postList(pageSize, data.value.length)
  20. .then((res) => {
  21. if (res.data.length < pageSize) {
  22. isEnd.value = true
  23. }
  24. data.value = [...data.value, ...res.data]
  25. })
  26. .catch((err) => {
  27. console.error(err)
  28. })
  29. }
  30. onMounted(() => {
  31. nextDatum()
  32. })
  33. const tokenAvailable = ref(false)
  34. const floatBtnElementAnimationStyle = ref({
  35. display: 'none',
  36. transform: 'scale(0)',
  37. transition: 'all .3s',
  38. })
  39. watch(
  40. () => tokenStore.available,
  41. async (newValue) => {
  42. if (!tokenAvailable.value && newValue) {
  43. tokenAvailable.value = true
  44. floatBtnElementAnimationStyle.value.display = 'block'
  45. floatBtnElementAnimationStyle.value.transform = 'scale(0)'
  46. await delay(20)
  47. floatBtnElementAnimationStyle.value.transform = 'scale(100%)'
  48. } else if (tokenAvailable.value && !newValue) {
  49. tokenAvailable.value = false
  50. floatBtnElementAnimationStyle.value.transform = 'scale(0)'
  51. await delay(320)
  52. floatBtnElementAnimationStyle.value.display = 'none'
  53. }
  54. },
  55. { immediate: true },
  56. )
  57. const tips: { content: string, value: string }[] = [
  58. { content: '有新技术?', value: '技术' },
  59. { content: '有新鲜事?', value: '日志' },
  60. { content: '有新想法?', value: '随笔' },
  61. { content: '写点东西?', value: '其他' },
  62. ]
  63. const tipIndex = ref(0)
  64. const currentTip = computed(() => tips[tipIndex.value])
  65. const changeTip = () => {
  66. tipIndex.value = (tipIndex.value + 1) % tips.length
  67. }
  68. function handleWriteClick() {
  69. router.push(`/editor?cate=${currentTip.value.value}`)
  70. }
  71. </script>
  72. <template>
  73. <div style="position: relative">
  74. <div
  75. class="float-btn"
  76. @click.stop="handleWriteClick"
  77. :style="floatBtnElementAnimationStyle"
  78. @mouseenter="changeTip"
  79. >
  80. <div class="float-btn-content">
  81. <div class="float-btn-ico" v-html="writeIcon.template" />
  82. <div>{{ currentTip.content }}</div>
  83. </div>
  84. </div>
  85. <PostCard v-for="item of data" :key="item.id" :target="item" />
  86. <div
  87. class="post-item"
  88. style="padding: 8px; display: flex; justify-content: center"
  89. v-if="isEnd"
  90. >
  91. <div>没有更多了😭</div>
  92. </div>
  93. <div class="post-item show-more" v-else @click="nextDatum">显示更多</div>
  94. </div>
  95. </template>
  96. <style scoped>
  97. .show-more {
  98. color: var(--secondary-text-color);
  99. padding: 8px;
  100. display: flex;
  101. justify-content: center;
  102. cursor: pointer;
  103. }
  104. .show-more:hover {
  105. color: var(--text-color);
  106. }
  107. .float-btn {
  108. position: fixed;
  109. right: 48px;
  110. bottom: 32px;
  111. padding: 8px;
  112. width: 32px;
  113. height: 32px;
  114. border-radius: 24px;
  115. overflow: hidden;
  116. fill: var(--text-color);
  117. color: var(--text-color);
  118. background-color: var(--secondary-background-color);
  119. box-shadow: rgba(0, 0, 0, 0.4) 0 0 6px;
  120. cursor: pointer;
  121. z-index: 10;
  122. }
  123. .float-btn:hover {
  124. fill: var(--secondary-background-color);
  125. background-color: var(--text-color);
  126. color: var(--secondary-background-color);
  127. right: 32px;
  128. bottom: 24px;
  129. width: 150px;
  130. height: 48px;
  131. border-radius: 32px;
  132. }
  133. .float-btn-content {
  134. width: 256px;
  135. display: flex;
  136. gap: 8px;
  137. font-size: large;
  138. font-weight: bold;
  139. align-items: center;
  140. }
  141. .float-btn-ico {
  142. width: 32px;
  143. height: 32px;
  144. transition: all 0.3s;
  145. }
  146. .float-btn:hover .float-btn-content .float-btn-ico {
  147. width: 48px;
  148. height: 48px;
  149. }
  150. </style>