feat: 更新文章功能和相关依赖
- 新增文章详情页面,支持根据文章 ID 加载和展示文章内容 - 添加文章卡片组件,展示推荐文章的标题、封面和阅读量 - 更新文章服务,支持获取文章列表和根据 ID 获取文章详情 - 集成腾讯云 COS SDK,支持文件上传功能 - 优化打卡功能,支持按日期加载和展示打卡记录 - 更新相关依赖,确保项目兼容性和功能完整性 - 调整样式以适应新功能的展示和交互
This commit is contained in:
126
app/article/[id].tsx
Normal file
126
app/article/[id].tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import { HeaderBar } from '@/components/ui/HeaderBar';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import { Article, getArticleById } from '@/services/articles';
|
||||
import dayjs from 'dayjs';
|
||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ScrollView, StyleSheet, Text, View, useWindowDimensions } from 'react-native';
|
||||
import RenderHTML from 'react-native-render-html';
|
||||
|
||||
export default function ArticleDetailScreen() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const router = useRouter();
|
||||
const [article, setArticle] = useState<Article | undefined>(undefined);
|
||||
const { width } = useWindowDimensions();
|
||||
const colorScheme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
||||
const theme = Colors[colorScheme];
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
getArticleById(id).then((article) => {
|
||||
console.log('article', article);
|
||||
setArticle(article);
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
if (!article) {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<HeaderBar title="文章" onBack={() => router.back()} showBottomBorder />
|
||||
<View style={{ padding: 24 }}>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const source = { html: wrapHtml(article.htmlContent) };
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: theme.surface }}>
|
||||
<HeaderBar title="文章" onBack={() => router.back()} showBottomBorder />
|
||||
<ScrollView contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={false}>
|
||||
<View style={styles.headerMeta}>
|
||||
<Text style={[styles.title, { color: theme.text }]}>{article.title}</Text>
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.metaText, { color: theme.textMuted }]}>{dayjs(article.publishedAt).format('YYYY-MM-DD')}</Text>
|
||||
<Text style={[styles.metaText, styles.dot]}>·</Text>
|
||||
<Text style={[styles.metaText, { color: theme.textMuted }]}>{article.readCount} 阅读</Text>
|
||||
</View>
|
||||
</View>
|
||||
<RenderHTML
|
||||
contentWidth={width - 48}
|
||||
source={source}
|
||||
baseStyle={{ ...htmlBaseStyles, color: theme.text }}
|
||||
tagsStyles={htmlTagStyles}
|
||||
enableExperimentalMarginCollapsing={true}
|
||||
/>
|
||||
<View style={{ height: 36 }} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function wrapHtml(inner: string) {
|
||||
// 为了统一排版与图片自适应
|
||||
return `
|
||||
<div class="article">
|
||||
${inner}
|
||||
</div>
|
||||
<style>
|
||||
.article img { max-width: 100%; height: auto; border-radius: 12px; }
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
contentContainer: {
|
||||
paddingHorizontal: 24,
|
||||
paddingTop: 12,
|
||||
},
|
||||
headerMeta: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
title: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
color: '#192126',
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: 8,
|
||||
},
|
||||
metaText: {
|
||||
fontSize: 12,
|
||||
color: '#8A8A8E',
|
||||
},
|
||||
dot: {
|
||||
paddingHorizontal: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const htmlBaseStyles = {
|
||||
color: '#192126',
|
||||
lineHeight: 24,
|
||||
fontSize: 16,
|
||||
} as const;
|
||||
|
||||
const htmlTagStyles = {
|
||||
h1: { fontSize: 26, fontWeight: '800', marginBottom: 8 },
|
||||
h2: { fontSize: 22, fontWeight: '800', marginTop: 8, marginBottom: 8 },
|
||||
h3: { fontSize: 18, fontWeight: '700', marginTop: 12, marginBottom: 6 },
|
||||
p: { marginBottom: 12 },
|
||||
ol: { marginBottom: 12, paddingLeft: 18 },
|
||||
ul: { marginBottom: 12, paddingLeft: 18 },
|
||||
li: { marginBottom: 6 },
|
||||
img: { marginTop: 8, marginBottom: 8, borderRadius: 12 },
|
||||
em: { fontStyle: 'italic' },
|
||||
strong: { fontWeight: '800' },
|
||||
} as const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user