You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
2.2 KiB
94 lines
2.2 KiB
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { articleApi } from '@/api/client'
|
|
import { ArticleStatus, type ArticleListItem } from '@/generated/api'
|
|
import ArticleCard from '@/components/ArticleCard.vue'
|
|
import Pagination from '@/components/Pagination.vue'
|
|
import TagCloud from '@/components/TagCloud.vue'
|
|
|
|
const route = useRoute()
|
|
|
|
const articles = ref<ArticleListItem[]>([])
|
|
const page = ref(1)
|
|
const size = 10
|
|
const totalPages = ref(0)
|
|
const loading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const tag = (route.query.tag as string) || undefined
|
|
const resp = await articleApi.apiArticlesGet(page.value, size, ArticleStatus.Published, tag)
|
|
const data = resp.data.data
|
|
articles.value = (data?.list as ArticleListItem[]) ?? []
|
|
totalPages.value = data?.totalPages ?? 0
|
|
} catch (e) {
|
|
console.error(e)
|
|
error.value = '文章加载失败,请稍后重试'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function onPageChange(p: number) {
|
|
page.value = p
|
|
load()
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<template>
|
|
<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-else-if="loading" class="hint-text">加载中...</p>
|
|
<p v-else-if="articles.length === 0" class="hint-text">暂无已发布文章</p>
|
|
<div v-else>
|
|
<ArticleCard v-for="article in articles" :key="article.id" :article="article" />
|
|
<Pagination :page="page" :total-pages="totalPages" @change="onPageChange" />
|
|
</div>
|
|
</div>
|
|
<aside class="home-sidebar">
|
|
<TagCloud />
|
|
</aside>
|
|
</div>
|
|
</template>
|
|
|
|
<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 {
|
|
font-family: var(--song-font-display);
|
|
font-size: 24px;
|
|
font-weight: 400;
|
|
letter-spacing: 0.04em;
|
|
margin: 0 0 20px;
|
|
}
|
|
|
|
.error-text {
|
|
color: var(--ac-danger);
|
|
}
|
|
|
|
.hint-text {
|
|
color: var(--ac-text-disabled);
|
|
}
|
|
</style>
|