|
|
|
@ -1,8 +1,8 @@ |
|
|
|
<script setup lang="ts"> |
|
|
|
import { ref, onMounted, watch } from 'vue' |
|
|
|
import { useRoute, useRouter } from 'vue-router' |
|
|
|
import { searchApi } from '@/api/client' |
|
|
|
import type { ArticleListItem } from '@/generated/api' |
|
|
|
import { searchApi, tagApi } from '@/api/client' |
|
|
|
import type { ArticleListItem, Tag } from '@/generated/api' |
|
|
|
import ArticleCard from '@/components/ArticleCard.vue' |
|
|
|
import Pagination from '@/components/Pagination.vue' |
|
|
|
|
|
|
|
@ -17,6 +17,23 @@ const totalPages = ref(0) |
|
|
|
const loading = ref(false) |
|
|
|
const error = ref<string | null>(null) |
|
|
|
|
|
|
|
const recommendedTags = ref<Tag[]>([]) |
|
|
|
|
|
|
|
async function loadRecommendedTags() { |
|
|
|
try { |
|
|
|
const resp = await tagApi.apiTagsGet() |
|
|
|
const tags = resp.data.data ?? [] |
|
|
|
recommendedTags.value = [...tags].sort((a, b) => b.articleCount - a.articleCount).slice(0, 8) |
|
|
|
} catch (e) { |
|
|
|
console.error(e) |
|
|
|
recommendedTags.value = [] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function goTag(tag: Tag) { |
|
|
|
router.push({ name: 'Home', query: { tag: tag.slug } }) |
|
|
|
} |
|
|
|
|
|
|
|
async function doSearch() { |
|
|
|
const q = keyword.value.trim() |
|
|
|
if (!q) { |
|
|
|
@ -61,6 +78,7 @@ watch( |
|
|
|
) |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
loadRecommendedTags() |
|
|
|
if (keyword.value) doSearch() |
|
|
|
}) |
|
|
|
</script> |
|
|
|
@ -72,6 +90,15 @@ onMounted(() => { |
|
|
|
<button class="search-btn" type="submit">搜索</button> |
|
|
|
</form> |
|
|
|
|
|
|
|
<div v-if="recommendedTags.length" class="search-recommend"> |
|
|
|
<span class="section-label">标签推荐</span> |
|
|
|
<div class="tag-chips"> |
|
|
|
<button v-for="tag in recommendedTags" :key="tag.id" class="tag-chip" @click="goTag(tag)"> |
|
|
|
{{ tag.name }} |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<p v-if="error" class="error-text">{{ error }}</p> |
|
|
|
<p v-else-if="loading" class="hint-text">搜索中...</p> |
|
|
|
<template v-else> |
|
|
|
@ -92,6 +119,42 @@ onMounted(() => { |
|
|
|
margin-bottom: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
.search-recommend { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
flex-wrap: wrap; |
|
|
|
gap: 10px; |
|
|
|
margin-bottom: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
.section-label { |
|
|
|
color: var(--ac-text-secondary); |
|
|
|
font-size: 13px; |
|
|
|
letter-spacing: 0.1em; |
|
|
|
} |
|
|
|
|
|
|
|
.tag-chips { |
|
|
|
display: flex; |
|
|
|
flex-wrap: wrap; |
|
|
|
gap: 8px; |
|
|
|
} |
|
|
|
|
|
|
|
.tag-chip { |
|
|
|
height: 26px; |
|
|
|
padding: 0 12px; |
|
|
|
background: var(--ac-primary-bg); |
|
|
|
color: var(--ac-primary-ink); |
|
|
|
border: 1px solid var(--ac-primary); |
|
|
|
border-radius: var(--song-radius-s); |
|
|
|
font-size: 13px; |
|
|
|
cursor: pointer; |
|
|
|
transition: background var(--song-dur-1) var(--song-ease); |
|
|
|
} |
|
|
|
|
|
|
|
.tag-chip:hover { |
|
|
|
background: var(--ac-active); |
|
|
|
} |
|
|
|
|
|
|
|
.search-input { |
|
|
|
flex: 1; |
|
|
|
padding: 10px 14px; |
|
|
|
|