feat: update ui
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<script setup>
|
||||
import ArticleListItem from '@/components/ArticleListItem.vue'
|
||||
import { useArticleStore } from '@/stores/article'
|
||||
import { useScroll } from '@vueuse/core'; // Changed to useScroll as a simpler alternative
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useIntersectionObserver } from '@vueuse/core'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -9,29 +10,36 @@ const store = useArticleStore()
|
||||
const searchInput = ref('')
|
||||
const loadingTrigger = ref(null)
|
||||
|
||||
const el = ref(null)
|
||||
const { y, isScrolling } = useScroll(el)
|
||||
|
||||
watch(y, (newY) => {
|
||||
if (!store.loading && !isScrolling && newY > 0) {
|
||||
const scrollHeight = el.value.scrollHeight
|
||||
const scrollTop = newY
|
||||
const clientHeight = el.value.clientHeight
|
||||
|
||||
if (scrollHeight - scrollTop - clientHeight < 50) {
|
||||
// 优化 Intersection Observer 配置
|
||||
useIntersectionObserver(
|
||||
loadingTrigger,
|
||||
([{ isIntersecting }]) => {
|
||||
console.log('Intersection state:', { isIntersecting, loading: store.loading, hasMore: store.hasMore })
|
||||
if (isIntersecting && !store.loading && store.hasMore) {
|
||||
console.log('Fetching more articles...')
|
||||
store.fetchArticles()
|
||||
}
|
||||
},
|
||||
{
|
||||
threshold: 0,
|
||||
rootMargin: '100px' // 提前 100px 触发加载
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const showArticle = (id) => {
|
||||
router.push(`/article/${id}`)
|
||||
router.push(`/posts/${id}`)
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
store.setSearchQuery(searchInput.value)
|
||||
}
|
||||
|
||||
const handleKeyup = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (store.articles.length === 0) {
|
||||
store.fetchArticles()
|
||||
@@ -40,21 +48,36 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="article-list" ref="el">
|
||||
<div class="search-bar p-2">
|
||||
<InputText v-model="searchInput" placeholder="搜索文章" class="w-full" />
|
||||
<Button @click="handleSearch">搜索</Button>
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="flex-none bg-white border-b border-gray-200 z-50">
|
||||
<div class="p-4">
|
||||
<input type="search" v-model="searchInput" @keyup="handleKeyup" placeholder="搜索文章"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="articles p-2">
|
||||
<Card v-for="article in store.articles" :key="article.id" class="mb-2 article-card"
|
||||
@click="showArticle(article.id)">
|
||||
<template #title>{{ article.title }}</template>
|
||||
<template #content>{{ article.summary }}</template>
|
||||
</Card>
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div class="p-4">
|
||||
<div v-if="store.articles.length === 0 && !store.loading" class="text-center text-gray-500 py-8">
|
||||
暂无文章
|
||||
</div>
|
||||
|
||||
<div ref="loadingTrigger" v-show="store.hasMore">
|
||||
<ProgressSpinner v-if="store.loading" />
|
||||
<ArticleListItem v-for="article in store.articles" :key="article.id" :article="article"
|
||||
@click="showArticle(article.id)" class="mb-4" />
|
||||
|
||||
<!-- 优化加载触发器位置和显示 -->
|
||||
<div ref="loadingTrigger" class="py-4 text-center" v-show="store.hasMore || store.loading">
|
||||
<div v-if="store.loading"
|
||||
class="animate-spin rounded-full h-8 w-8 border-4 border-gray-200 border-t-blue-600 mx-auto">
|
||||
</div>
|
||||
<div v-else class="h-8">
|
||||
<!-- 空白占位,保持触发器可见 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!store.hasMore && store.articles.length > 0" class="text-center text-gray-500 py-4">
|
||||
没有更多文章了
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user