|
@@ -0,0 +1,206 @@
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import type { PostSketch, Tag } from '@/models'
|
|
|
|
+import { computed, onMounted, ref, watch } from 'vue'
|
|
|
|
+import { api } from '@/utils/axios'
|
|
|
|
+import { useTokenStore } from '@/stores/auth.ts'
|
|
|
|
+import { colorMatchTheme, delay, formateDateAccurateToDay } from '@/utils'
|
|
|
|
+import { writeIcon } from '@/assets/icons.ts'
|
|
|
|
+import { getQuery, router } from '@/router'
|
|
|
|
+import { optionOfCate } from '@/utils/cates.ts'
|
|
|
|
+import TagCard from '@/components/TagCard.vue'
|
|
|
|
+
|
|
|
|
+const data = ref<PostSketch[]>([])
|
|
|
|
+const targetTag = ref<Tag | null>(null)
|
|
|
|
+
|
|
|
|
+const tokenStore = useTokenStore()
|
|
|
|
+
|
|
|
|
+const archiveType = ref<'cate' | 'tag' | undefined>(undefined)
|
|
|
|
+
|
|
|
|
+const cate = ref('')
|
|
|
|
+let tag = ''
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ cate.value = getQuery('cate')
|
|
|
|
+ tag = getQuery('tag')
|
|
|
|
+ if ((!cate.value && !tag) || (cate.value && tag)) {
|
|
|
|
+ router.push('/archives')
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if (cate.value) {
|
|
|
|
+ archiveType.value = 'cate'
|
|
|
|
+ api.postsOfCate(cate.value).then((res) => {
|
|
|
|
+ data.value = res.data
|
|
|
|
+ })
|
|
|
|
+ } else if (tag) {
|
|
|
|
+ archiveType.value = 'tag'
|
|
|
|
+ api.postsOfTag(tag).then((res) => {
|
|
|
|
+ data.value = res.data.posts
|
|
|
|
+ targetTag.value = res.data.tag
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const tokenAvailable = ref(false)
|
|
|
|
+
|
|
|
|
+const floatBtnElementAnimationStyle = ref({
|
|
|
|
+ display: 'none',
|
|
|
|
+ transform: 'scale(0)',
|
|
|
|
+ transition: 'all .3s',
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+watch(
|
|
|
|
+ () => tokenStore.available,
|
|
|
|
+ async (newValue) => {
|
|
|
|
+ if (!tokenAvailable.value && newValue) {
|
|
|
|
+ tokenAvailable.value = true
|
|
|
|
+ floatBtnElementAnimationStyle.value.display = 'block'
|
|
|
|
+ floatBtnElementAnimationStyle.value.transform = 'scale(0)'
|
|
|
|
+ await delay(20)
|
|
|
|
+ floatBtnElementAnimationStyle.value.transform = 'scale(100%)'
|
|
|
|
+ } else if (tokenAvailable.value && !newValue) {
|
|
|
|
+ tokenAvailable.value = false
|
|
|
|
+ floatBtnElementAnimationStyle.value.transform = 'scale(0)'
|
|
|
|
+ await delay(320)
|
|
|
|
+ floatBtnElementAnimationStyle.value.display = 'none'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ { immediate: true },
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+function handleWriteClick() {
|
|
|
|
+ if (archiveType.value == 'tag') router.push(`/editor?tag=${tag}`)
|
|
|
|
+ else if (archiveType.value == 'cate') router.push(`/editor?cate=${cate.value}`)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const cateOption = computed(() => optionOfCate(cate.value))
|
|
|
|
+
|
|
|
|
+const handlePostLineClick = (post: PostSketch) => {
|
|
|
|
+ router.push(`/detail?id=${post.id}`)
|
|
|
|
+}
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <div style="position: relative">
|
|
|
|
+ <div class="float-btn" @click.stop="handleWriteClick" :style="floatBtnElementAnimationStyle">
|
|
|
|
+ <div class="float-btn-ico" v-html="writeIcon.template" />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="post-item" style="display: flex; flex-direction: column; gap: 12px">
|
|
|
|
+ <div class="cate-title" v-if="archiveType == 'cate'">
|
|
|
|
+ <div class="cate-icon" v-html="cateOption.svg.template" />
|
|
|
|
+ <div class="cate-content">{{ cateOption.label }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="tag-title" v-else-if="archiveType == 'tag' && targetTag">
|
|
|
|
+ <div class="tag-title-content">正在浏览话题包含</div>
|
|
|
|
+ <TagCard :target="targetTag" />
|
|
|
|
+ <div class="tag-title-content">的文章</div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="post-line" v-for="item of data" :key="item.id" @click="handlePostLineClick(item)">
|
|
|
|
+ <div class="post-line-title">{{ item.title }}</div>
|
|
|
|
+ <div class="post-line-date">{{ formateDateAccurateToDay(item.createdAt, false) }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<style scoped>
|
|
|
|
+.cate-title {
|
|
|
|
+ color: var(--text-color);
|
|
|
|
+ display: flex;
|
|
|
|
+ width: 100%;
|
|
|
|
+ align-items: center;
|
|
|
|
+ gap: 8px;
|
|
|
|
+ padding-bottom: 4px;
|
|
|
|
+ background: linear-gradient(to right, var(--secondary-text-color), var(--secondary-text-color)) no-repeat bottom left;
|
|
|
|
+ background-size: 100% 1px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.cate-icon {
|
|
|
|
+ width: 24px;
|
|
|
|
+ height: 24px;
|
|
|
|
+ padding-left: 4px;
|
|
|
|
+ fill: var(--text-color);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.cate-content {
|
|
|
|
+ font-size: large;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.tag-title {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: end;
|
|
|
|
+ gap: 12px;
|
|
|
|
+ color: var(--text-color);
|
|
|
|
+ width: 100%;
|
|
|
|
+ padding-bottom: 4px;
|
|
|
|
+ background: linear-gradient(to right, var(--secondary-text-color), var(--secondary-text-color)) no-repeat bottom left;
|
|
|
|
+ background-size: 100% 1px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.tag-title-content {
|
|
|
|
+ font-size: large;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.post-line {
|
|
|
|
+ display: flex;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.post-line-title {
|
|
|
|
+ color: var(--text-color);
|
|
|
|
+ background: linear-gradient(to right, var(--text-color), var(--text-color)) no-repeat left bottom;
|
|
|
|
+ background-size: 0 2px;
|
|
|
|
+ transition: background-size 0.2s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.post-line:hover .post-line-title {
|
|
|
|
+ background-size: 100% 2px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.post-line .post-line-date {
|
|
|
|
+ color: var(--secondary-text-color);
|
|
|
|
+ background: linear-gradient(to right, var(--secondary-text-color), var(--secondary-text-color))
|
|
|
|
+ no-repeat right bottom;
|
|
|
|
+ background-size: 0 1px;
|
|
|
|
+ transition: background-size 0.2s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.post-line:hover .post-line-date {
|
|
|
|
+ background-size: 100% 1px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.float-btn {
|
|
|
|
+ position: fixed;
|
|
|
|
+ right: 48px;
|
|
|
|
+ bottom: 32px;
|
|
|
|
+ padding: 8px;
|
|
|
|
+ width: 32px;
|
|
|
|
+ height: 32px;
|
|
|
|
+ border-radius: 50%;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ fill: var(--text-color);
|
|
|
|
+ background-color: var(--secondary-background-color);
|
|
|
|
+ box-shadow: rgba(0, 0, 0, 0.4) 0 0 6px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ z-index: 10;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.float-btn:hover {
|
|
|
|
+ fill: var(--secondary-background-color);
|
|
|
|
+ background-color: var(--text-color);
|
|
|
|
+ width: 48px;
|
|
|
|
+ height: 48px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.float-btn-ico {
|
|
|
|
+ width: 32px;
|
|
|
|
+ height: 32px;
|
|
|
|
+ transition: all 0.3s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.float-btn:hover .float-btn-ico {
|
|
|
|
+ width: 48px;
|
|
|
|
+ height: 48px;
|
|
|
|
+}
|
|
|
|
+</style>
|