Files
nl-blogs/client/src/templates/guofeng/pages/Blog.vue
2026-08-24 13:11:21 +08:00

96 lines
2.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<div class="gf-toolbar">
<input v-model="keyword" class="gf-input" placeholder="搜文章…" @keyup.enter="load(1)" />
<button class="gf-btn" @click="load(1)">检索</button>
<select v-model="selectedCat" class="gf-input" @change="load(1)">
<option :value="0">全部</option>
<option v-for="c in categories" :key="c.id" :value="c.id">{{ c.name }}</option>
</select>
</div>
<div v-if="loading" style="padding:40px 0;color:var(--g-muted);">研墨中</div>
<template v-else>
<div class="ink-grid">
<InkCard v-for="p in posts" :key="p.id" :to="`/blog/${p.id}`" :seed="p.id">
<span class="ink-cat">{{ p.categoryName || '未分类' }} · {{ p.date }}</span>
<span class="ink-title">{{ p.title }}</span>
<span class="ink-desc">{{ p.excerpt || '(暂无摘要)' }}</span>
</InkCard>
</div>
<div v-if="!posts.length" style="padding:40px 0;color:var(--g-muted);">暂无文章</div>
<div v-if="totalPages > 1" class="ink-pager">
<button class="gf-btn" :disabled="page <= 1" @click="load(page - 1)"> 上一页</button>
<span style="align-self:center;color:var(--g-muted);font-size:13px;letter-spacing:0.2em;"> {{ page }} / {{ totalPages }} </span>
<button class="gf-btn" :disabled="page >= totalPages" @click="load(page + 1)">下一页 </button>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { fetchPosts, fetchCategories, type Post } from '../../../services/api'
import InkCard from '../components/InkCard.vue'
const posts = ref<Post[]>([])
const categories = ref<Awaited<ReturnType<typeof fetchCategories>>>([])
const keyword = ref('')
const selectedCat = ref(0)
const page = ref(1)
const totalPages = ref(1)
const loading = ref(true)
const PAGE_SIZE = 8
const load = async (p: number) => {
loading.value = true
const res = await fetchPosts(keyword.value || undefined, selectedCat.value > 0 ? selectedCat.value : undefined, undefined, undefined, p, PAGE_SIZE)
const list = 'list' in res ? res.list : (Array.isArray(res) ? res : [])
const total = 'total' in res ? res.total : list.length
posts.value = list
totalPages.value = Math.max(1, Math.ceil(total / PAGE_SIZE))
page.value = p
loading.value = false
}
onMounted(async () => {
categories.value = await fetchCategories()
await load(1)
})
</script>
<style scoped>
.gf-toolbar {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 22px;
}
.ink-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 22px;
}
.ink-pager {
display: flex;
gap: 14px;
justify-content: center;
margin-top: 30px;
}
.gf-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
@media (max-width: 760px) {
.ink-grid {
grid-template-columns: 1fr;
}
}
</style>