2 changed files with 148 additions and 9 deletions
@ -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> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue