feat: update ui

This commit is contained in:
yanghao05
2025-04-16 21:54:27 +08:00
parent 85ece3e899
commit 682a2397d2
17 changed files with 525 additions and 223 deletions

View File

@@ -14,14 +14,31 @@ onMounted(async () => {
</script>
<template>
<div class="article-detail p-3">
<Button icon="pi pi-arrow-left" @click="router.back()" class="p-button-text mb-3" />
<div class="min-h-screen bg-gray-50">
<header class="fixed top-0 left-0 right-0 h-14 bg-white border-b border-gray-200 flex items-center px-4 z-50">
<button @click="router.back()"
class="flex items-center justify-center w-10 h-10 mr-2 rounded-full hover:bg-gray-100 active:bg-gray-200 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</button>
<h2 class="text-lg font-medium">{{ article?.title || '文章详情' }}</h2>
</header>
<div v-if="article">
<h1>{{ article.title }}</h1>
<div class="content mt-3">
{{ article.content }}
<main class="pt-14 px-4">
<div v-if="article" class="py-4">
<div class="prose max-w-none">
{{ article.content }}
</div>
</div>
</div>
<div v-else class="flex justify-center items-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-4 border-gray-200 border-t-blue-600"></div>
</div>
</main>
</div>
</template>
<style scoped>
/* 可以移除所有样式,因为都使用了 Tailwind 类 */
</style>

View File

@@ -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>

View File

@@ -16,33 +16,21 @@ onMounted(async () => {
</script>
<template>
<div class="purchased-articles">
<h2>已购买的文章</h2>
<div class="articles-list">
<div v-if="purchasedArticles.length === 0" class="empty-state">
<div class="p-4">
<h2 class="text-xl font-medium mb-4">已购买的文章</h2>
<div class="space-y-4">
<div v-if="purchasedArticles.length === 0" class="py-12 text-center text-gray-500">
暂无已购买的文章
</div>
<Card v-else v-for="article in purchasedArticles" :key="article.id" class="article-card"
@click="showArticle(article.id)">
<template #title>{{ article.title }}</template>
<template #content>{{ article.summary }}</template>
</Card>
<div v-else v-for="article in purchasedArticles" :key="article.id" @click="showArticle(article.id)"
class="bg-white rounded-lg shadow-sm p-4 hover:shadow-md transition-shadow cursor-pointer">
<h3 class="text-lg font-medium mb-2">{{ article.title }}</h3>
<p class="text-gray-600">{{ article.summary }}</p>
</div>
</div>
</div>
</template>
<style scoped>
.purchased-articles {
padding: 1rem;
}
.empty-state {
text-align: center;
padding: 2rem;
color: #666;
}
.article-card {
margin-bottom: 1rem;
}
/* Remove all styles as they're replaced by Tailwind classes */
</style>

View File

@@ -9,21 +9,21 @@ const userInfo = ref({
</script>
<template>
<div class="user-profile p-3">
<Card>
<template #header>
<div class="flex align-items-center gap-3">
<Avatar :image="userInfo.avatar" size="large" />
<h3>{{ userInfo.name }}</h3>
<div class="p-4">
<div class="bg-white rounded-lg shadow-sm p-4">
<div class="flex items-center gap-4 p-4 border-b border-gray-100">
<div class="w-16 h-16 rounded-full bg-gray-200 overflow-hidden">
<img v-if="userInfo.avatar" :src="userInfo.avatar" alt="头像" class="w-full h-full object-cover">
</div>
</template>
<template #content>
<div class="grid">
<Button label="我的收藏" class="p-button-text" />
<Button label="我的点赞" class="p-button-text" />
<Button label="订单列表" class="p-button-text" />
</div>
</template>
</Card>
<h3 class="text-xl font-medium">{{ userInfo.name }}</h3>
</div>
<div class="grid grid-cols-3 gap-4 p-4">
<button v-for="(item, index) in ['我的收藏', '我的点赞', '订单列表']" :key="index"
class="py-3 text-center text-gray-700 hover:bg-gray-50 active:bg-gray-100 rounded-lg transition-colors">
{{ item }}
</button>
</div>
</div>
</div>
</template>