Browse Source

feat(component): add TagCloud with count-based sizing

main
Mohan 1 month ago
parent
commit
3e8ba9f8ed
  1. 113
      src/components/TagCloud.vue
  2. 32
      src/views/HomeView.vue

113
src/components/TagCloud.vue

@ -0,0 +1,113 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { tagApi } from '@/api/client'
import type { Tag } from '@/generated/api'
const router = useRouter()
const tags = ref<Tag[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
function sizeClass(count: number): string {
if (count >= 5) return 'tag-lg'
if (count >= 2) return 'tag-md'
return 'tag-sm'
}
function goToTag(tag: Tag) {
router.push({ name: 'Home', query: { tag: tag.slug } })
}
async function load() {
loading.value = true
error.value = null
try {
const resp = await tagApi.apiTagsGet()
tags.value = resp.data.data ?? []
} catch (e) {
console.error(e)
error.value = '标签加载失败'
} finally {
loading.value = false
}
}
onMounted(load)
</script>
<template>
<div class="tag-cloud">
<h3 class="tag-cloud-title">标签云</h3>
<p v-if="error" class="error-text">{{ error }}</p>
<p v-else-if="loading" class="hint-text">加载中...</p>
<p v-else-if="tags.length === 0" class="hint-text">暂无标签</p>
<div v-else class="tag-cloud-items">
<button
v-for="tag in tags"
:key="tag.id"
class="tag-item"
:class="sizeClass(tag.articleCount)"
@click="goToTag(tag)"
>
{{ tag.name }}
</button>
</div>
</div>
</template>
<style scoped>
.tag-cloud {
background: #fff;
border: 1px solid #e5e5ea;
border-radius: 8px;
padding: 16px;
}
.tag-cloud-title {
margin: 0 0 12px;
font-size: 16px;
}
.tag-cloud-items {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag-item {
border: none;
background: #eef3ff;
color: #3b5bdb;
border-radius: 12px;
cursor: pointer;
font-size: 13px;
padding: 4px 12px;
}
.tag-item:hover {
background: #dbe4ff;
}
.tag-lg {
font-size: 17px;
padding: 6px 16px;
}
.tag-md {
font-size: 14px;
padding: 5px 14px;
}
.tag-sm {
font-size: 12px;
}
.error-text {
color: #d33c3c;
}
.hint-text {
color: #8a93a3;
}
</style>

32
src/views/HomeView.vue

@ -1,9 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { articleApi } from '@/api/client' import { articleApi } from '@/api/client'
import { ArticleStatus, type ArticleListItem } from '@/generated/api' import { ArticleStatus, type ArticleListItem } from '@/generated/api'
import ArticleCard from '@/components/ArticleCard.vue' import ArticleCard from '@/components/ArticleCard.vue'
import Pagination from '@/components/Pagination.vue' import Pagination from '@/components/Pagination.vue'
import TagCloud from '@/components/TagCloud.vue'
const route = useRoute()
const articles = ref<ArticleListItem[]>([]) const articles = ref<ArticleListItem[]>([])
const page = ref(1) const page = ref(1)
@ -16,7 +20,8 @@ async function load() {
loading.value = true loading.value = true
error.value = null error.value = null
try { try {
const resp = await articleApi.apiArticlesGet(page.value, size, ArticleStatus.Published)
const tag = (route.query.tag as string) || undefined
const resp = await articleApi.apiArticlesGet(page.value, size, ArticleStatus.Published, tag)
const data = resp.data.data const data = resp.data.data
articles.value = (data?.list as ArticleListItem[]) ?? [] articles.value = (data?.list as ArticleListItem[]) ?? []
totalPages.value = data?.totalPages ?? 0 totalPages.value = data?.totalPages ?? 0
@ -37,8 +42,9 @@ onMounted(load)
</script> </script>
<template> <template>
<div>
<h1 class="page-title">最新文章</h1>
<div class="home-layout">
<div class="home-main">
<h1 class="page-title">{{ route.query.tag ? '筛选结果' : '最新文章' }}</h1>
<p v-if="error" class="error-text">{{ error }}</p> <p v-if="error" class="error-text">{{ error }}</p>
<p v-else-if="loading" class="hint-text">加载中...</p> <p v-else-if="loading" class="hint-text">加载中...</p>
<p v-else-if="articles.length === 0" class="hint-text">暂无已发布文章</p> <p v-else-if="articles.length === 0" class="hint-text">暂无已发布文章</p>
@ -47,9 +53,29 @@ onMounted(load)
<Pagination :page="page" :total-pages="totalPages" @change="onPageChange" /> <Pagination :page="page" :total-pages="totalPages" @change="onPageChange" />
</div> </div>
</div> </div>
<aside class="home-sidebar">
<TagCloud />
</aside>
</div>
</template> </template>
<style scoped> <style scoped>
.home-layout {
display: flex;
gap: 24px;
align-items: flex-start;
}
.home-main {
flex: 1;
min-width: 0;
}
.home-sidebar {
width: 260px;
flex-shrink: 0;
}
.page-title { .page-title {
font-size: 24px; font-size: 24px;
margin: 0 0 20px; margin: 0 0 20px;

Loading…
Cancel
Save