ArchiveDetailView.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <script setup lang="ts">
  2. import type { PostSketch, Tag } from '@/models'
  3. import { computed, onMounted, ref, watch } from 'vue'
  4. import { api } from '@/utils/axios'
  5. import { useTokenStore } from '@/stores/auth.ts'
  6. import { colorMatchTheme, delay, formateDateAccurateToDay } from '@/utils'
  7. import { writeIcon } from '@/assets/icons.ts'
  8. import { getQuery, router } from '@/router'
  9. import { optionOfCate } from '@/utils/cates.ts'
  10. import TagCard from '@/components/TagCard.vue'
  11. const data = ref<PostSketch[]>([])
  12. const targetTag = ref<Tag | null>(null)
  13. const tokenStore = useTokenStore()
  14. const archiveType = ref<'cate' | 'tag' | undefined>(undefined)
  15. const cate = ref('')
  16. let tag = ''
  17. onMounted(() => {
  18. cate.value = getQuery('cate')
  19. tag = getQuery('tag')
  20. if ((!cate.value && !tag) || (cate.value && tag)) {
  21. router.push('/archives')
  22. return
  23. }
  24. if (cate.value) {
  25. archiveType.value = 'cate'
  26. api.postsOfCate(cate.value).then((res) => {
  27. data.value = res.data
  28. })
  29. } else if (tag) {
  30. archiveType.value = 'tag'
  31. api.postsOfTag(tag).then((res) => {
  32. data.value = res.data.posts
  33. targetTag.value = res.data.tag
  34. })
  35. }
  36. })
  37. const tokenAvailable = ref(false)
  38. const floatBtnElementAnimationStyle = ref({
  39. display: 'none',
  40. transform: 'scale(0)',
  41. transition: 'all .3s',
  42. })
  43. watch(
  44. () => tokenStore.available,
  45. async (newValue) => {
  46. if (!tokenAvailable.value && newValue) {
  47. tokenAvailable.value = true
  48. floatBtnElementAnimationStyle.value.display = 'block'
  49. floatBtnElementAnimationStyle.value.transform = 'scale(0)'
  50. await delay(20)
  51. floatBtnElementAnimationStyle.value.transform = 'scale(100%)'
  52. } else if (tokenAvailable.value && !newValue) {
  53. tokenAvailable.value = false
  54. floatBtnElementAnimationStyle.value.transform = 'scale(0)'
  55. await delay(320)
  56. floatBtnElementAnimationStyle.value.display = 'none'
  57. }
  58. },
  59. { immediate: true },
  60. )
  61. function handleWriteClick() {
  62. if (archiveType.value == 'tag') router.push(`/editor?tag=${tag}`)
  63. else if (archiveType.value == 'cate') router.push(`/editor?cate=${cate.value}`)
  64. }
  65. const cateOption = computed(() => optionOfCate(cate.value))
  66. const handlePostLineClick = (post: PostSketch) => {
  67. router.push(`/detail?id=${post.id}`)
  68. }
  69. </script>
  70. <template>
  71. <div style="position: relative">
  72. <div class="float-btn" @click.stop="handleWriteClick" :style="floatBtnElementAnimationStyle">
  73. <div class="float-btn-ico" v-html="writeIcon.template" />
  74. </div>
  75. <div class="post-item" style="display: flex; flex-direction: column; gap: 12px">
  76. <div class="cate-title" v-if="archiveType == 'cate'">
  77. <div class="cate-icon" v-html="cateOption.svg.template" />
  78. <div class="cate-content">{{ cateOption.label }}</div>
  79. </div>
  80. <div class="tag-title" v-else-if="archiveType == 'tag' && targetTag">
  81. <div class="tag-title-content">正在浏览话题包含</div>
  82. <TagCard :target="targetTag" />
  83. <div class="tag-title-content">的文章</div>
  84. </div>
  85. <div class="post-line" v-for="item of data" :key="item.id" @click="handlePostLineClick(item)">
  86. <div class="post-line-title">{{ item.title }}</div>
  87. <div class="post-line-date">{{ formateDateAccurateToDay(item.createdAt, false) }}</div>
  88. </div>
  89. </div>
  90. </div>
  91. </template>
  92. <style scoped>
  93. .cate-title {
  94. color: var(--text-color);
  95. display: flex;
  96. width: 100%;
  97. align-items: center;
  98. gap: 8px;
  99. padding-bottom: 4px;
  100. background: linear-gradient(to right, var(--secondary-text-color), var(--secondary-text-color)) no-repeat bottom left;
  101. background-size: 100% 1px;
  102. }
  103. .cate-icon {
  104. width: 24px;
  105. height: 24px;
  106. padding-left: 4px;
  107. fill: var(--text-color);
  108. }
  109. .cate-content {
  110. font-size: large;
  111. }
  112. .tag-title {
  113. display: flex;
  114. align-items: end;
  115. gap: 12px;
  116. color: var(--text-color);
  117. width: 100%;
  118. padding-bottom: 4px;
  119. background: linear-gradient(to right, var(--secondary-text-color), var(--secondary-text-color)) no-repeat bottom left;
  120. background-size: 100% 1px;
  121. }
  122. .tag-title-content {
  123. font-size: large;
  124. }
  125. .post-line {
  126. display: flex;
  127. cursor: pointer;
  128. justify-content: space-between;
  129. }
  130. .post-line-title {
  131. color: var(--text-color);
  132. background: linear-gradient(to right, var(--text-color), var(--text-color)) no-repeat left bottom;
  133. background-size: 0 2px;
  134. transition: background-size 0.2s;
  135. }
  136. .post-line:hover .post-line-title {
  137. background-size: 100% 2px;
  138. }
  139. .post-line .post-line-date {
  140. color: var(--secondary-text-color);
  141. background: linear-gradient(to right, var(--secondary-text-color), var(--secondary-text-color))
  142. no-repeat right bottom;
  143. background-size: 0 1px;
  144. transition: background-size 0.2s;
  145. }
  146. .post-line:hover .post-line-date {
  147. background-size: 100% 1px;
  148. }
  149. .float-btn {
  150. position: fixed;
  151. right: 48px;
  152. bottom: 32px;
  153. padding: 8px;
  154. width: 32px;
  155. height: 32px;
  156. border-radius: 50%;
  157. overflow: hidden;
  158. fill: var(--text-color);
  159. background-color: var(--secondary-background-color);
  160. box-shadow: rgba(0, 0, 0, 0.4) 0 0 6px;
  161. cursor: pointer;
  162. z-index: 10;
  163. }
  164. .float-btn:hover {
  165. fill: var(--secondary-background-color);
  166. background-color: var(--text-color);
  167. width: 48px;
  168. height: 48px;
  169. }
  170. .float-btn-ico {
  171. width: 32px;
  172. height: 32px;
  173. transition: all 0.3s;
  174. }
  175. .float-btn:hover .float-btn-ico {
  176. width: 48px;
  177. height: 48px;
  178. }
  179. </style>