refactor(mp): 迁移文章列表页 — 使用统一组件库

- PageShell 替代手写 min-height/bg/padding
- ContentCard 替代手写 article-card 卡片样式
- LoadingCard 替代初始加载态
- 精简 SCSS 删除已接管样式,保留分类筛选/卡片内容布局

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
iven
2026-05-16 00:55:24 +08:00
parent 80794c9547
commit 9728afbc1b
2 changed files with 54 additions and 80 deletions

View File

@@ -1,13 +1,11 @@
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
.article-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
// ContentCard 已接管article-card 背景/圆角/阴影/触摸反馈
// LoadingCard 已接管:初始加载骨架屏
// EmptyState 已接管:空状态样式
/* ─── 分类筛选滚动条(页面特有) ─── */
.article-categories {
white-space: nowrap;
margin-bottom: 24px;
@@ -31,20 +29,14 @@
}
}
/* ─── 文章列表布局 ─── */
.article-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.article-card {
display: flex;
background: $card;
border-radius: $r;
padding: 28px;
box-shadow: $shadow-sm;
}
/* ─── 文章卡片内容ContentCard 已接管外层卡片样式) ─── */
.article-card-body {
flex: 1;
display: flex;
@@ -107,25 +99,3 @@
width: 100%;
height: 100%;
}
.empty-state {
display: flex;
justify-content: center;
align-items: center;
padding: 120px 0;
}
.empty-text {
font-size: var(--tk-font-body-lg);
color: var(--tk-text-secondary);
}
.loading-hint {
text-align: center;
padding: 24px 0;
}
.loading-text {
font-size: var(--tk-font-h2);
color: var(--tk-text-secondary);
}

View File

@@ -3,9 +3,12 @@ import { View, Text, Image, ScrollView } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { listArticles, listCategories, Article, ArticleCategory } from '../../services/article';
import EmptyState from '../../components/EmptyState';
import ErrorState from '../../components/ErrorState';
import Loading from '../../components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import LoadingCard from '@/components/ui/LoadingCard';
import EmptyState from '@/components/EmptyState';
import ErrorState from '@/components/ErrorState';
import Loading from '@/components/Loading';
import { useElderClass } from '../../hooks/useElderClass';
import './index.scss';
@@ -73,8 +76,12 @@ export default function ArticleList() {
Taro.navigateTo({ url: `/pages/article/detail/index?id=${id}` });
};
if (!loading && articles.length === 0 && !error && !categories.length) {
return <LoadingCard count={3} />;
}
return (
<View className={`article-page ${modeClass}`}>
<PageShell safeBottom className={modeClass}>
{/* 分类筛选 */}
{categories.length > 0 && (
<ScrollView scrollX className='article-categories'>
@@ -96,47 +103,44 @@ export default function ArticleList() {
</ScrollView>
)}
<View className='article-list'>
{error ? (
<ErrorState onRetry={() => fetchData(1, false, null)} />
) : articles.map((a) => (
<View
className='article-card'
key={a.id}
onClick={() => goToDetail(a.id)}
>
<View className='article-card-body'>
<Text className='article-card-title'>{a.title}</Text>
{a.summary && (
<Text className='article-card-summary'>{a.summary}</Text>
)}
<View className='article-card-meta'>
{(a.category_name || a.category) && (
<Text className='article-card-tag'>{a.category_name || a.category}</Text>
)}
{a.published_at && (
<Text className='article-card-date'>
{a.published_at.slice(0, 10)}
</Text>
)}
</View>
</View>
{a.cover_image && (
<View className='article-card-cover'>
<Image className='cover-img' src={a.cover_image} mode='aspectFill' lazyLoad />
</View>
)}
</View>
))}
</View>
{articles.length === 0 && !loading && (
{error ? (
<ErrorState onRetry={() => fetchData(1, false, null)} />
) : articles.length === 0 && !loading ? (
<EmptyState text='暂无资讯文章' />
) : (
<View className='article-list'>
{articles.map((a) => (
<ContentCard
key={a.id}
onPress={() => goToDetail(a.id)}
>
<View className='article-card-body'>
<Text className='article-card-title'>{a.title}</Text>
{a.summary && (
<Text className='article-card-summary'>{a.summary}</Text>
)}
<View className='article-card-meta'>
{(a.category_name || a.category) && (
<Text className='article-card-tag'>{a.category_name || a.category}</Text>
)}
{a.published_at && (
<Text className='article-card-date'>
{a.published_at.slice(0, 10)}
</Text>
)}
</View>
</View>
{a.cover_image && (
<View className='article-card-cover'>
<Image className='cover-img' src={a.cover_image} mode='aspectFill' lazyLoad />
</View>
)}
</ContentCard>
))}
</View>
)}
{loading && (
<Loading />
)}
</View>
{loading && <Loading />}
</PageShell>
);
}