Browse Source
- 薪资计算器:新增城市社保/公积金基数上下限表,选城市后 clamp 社保基数并计入计算(断点②) - 文书模板:新增模板正文与预览页 /legal/template,正文 selectable 长按可复制(断点③) - 分层:types/constants/api/页面;tsc --noEmit 0 错误、eslint 0 警告、路由审计 0 死链master
8 changed files with 383 additions and 14 deletions
@ -0,0 +1,145 @@ |
|||||
|
import { Ionicons } from '@expo/vector-icons'; |
||||
|
import { useRouter, useLocalSearchParams } from 'expo-router'; |
||||
|
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; |
||||
|
import { SafeAreaView } from 'react-native-safe-area-context'; |
||||
|
import { useLegalData } from '../../src/hooks'; |
||||
|
import { LoadingState, ErrorState, EmptyState } from '../../src/components/StateView'; |
||||
|
import { colors } from '../../src/theme'; |
||||
|
|
||||
|
/* ===================== 顶部导航 ===================== */ |
||||
|
|
||||
|
function Header({ onBack }: { onBack: () => void }) { |
||||
|
return ( |
||||
|
<View style={styles.header}> |
||||
|
<Pressable style={styles.headerBtn} onPress={onBack} hitSlop={8}> |
||||
|
<Ionicons name="chevron-back" size={24} color={colors.textPrimary} /> |
||||
|
</Pressable> |
||||
|
<Text style={styles.headerTitle}>文书预览</Text> |
||||
|
<View style={styles.headerBtn} /> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/* ===================== 页面 ===================== */ |
||||
|
|
||||
|
export default function LegalTemplateScreen() { |
||||
|
const router = useRouter(); |
||||
|
const { id } = useLocalSearchParams(); |
||||
|
const { templates, loading, error, refresh } = useLegalData(); |
||||
|
|
||||
|
const templateId = Array.isArray(id) ? id[0] : id; |
||||
|
const template = templateId ? templates.find((t) => t.id === templateId) : undefined; |
||||
|
|
||||
|
return ( |
||||
|
<SafeAreaView style={styles.safeArea} edges={['top']}> |
||||
|
<Header onBack={() => router.back()} /> |
||||
|
{loading ? ( |
||||
|
<LoadingState text="加载模板中…" /> |
||||
|
) : error ? ( |
||||
|
<ErrorState message="加载失败,请重试" onRetry={refresh} /> |
||||
|
) : !template ? ( |
||||
|
<EmptyState icon="document-text-outline" title="未找到该模板" subtitle="请返回重新选择" /> |
||||
|
) : ( |
||||
|
<ScrollView |
||||
|
style={styles.scroll} |
||||
|
contentContainerStyle={styles.content} |
||||
|
showsVerticalScrollIndicator={false} |
||||
|
> |
||||
|
<View style={styles.headCard}> |
||||
|
<Ionicons name={template.icon as never} size={28} color={colors.primary} /> |
||||
|
<View style={styles.headBody}> |
||||
|
<Text style={styles.name}>{template.name}</Text> |
||||
|
<Text style={styles.desc}>{template.description}</Text> |
||||
|
</View> |
||||
|
</View> |
||||
|
|
||||
|
<View style={styles.contentCard}> |
||||
|
{/* selectable:长按正文即可复制,无需额外原生模块(mock 阶段) */} |
||||
|
<Text style={styles.contentText} selectable> |
||||
|
{template.content} |
||||
|
</Text> |
||||
|
</View> |
||||
|
|
||||
|
<Text style={styles.tip}> |
||||
|
长按正文即可复制;正式下载 / 导出为 Word·PDF 需后端文件支持,当前为内容预览。 |
||||
|
</Text> |
||||
|
</ScrollView> |
||||
|
)} |
||||
|
</SafeAreaView> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
safeArea: { |
||||
|
flex: 1, |
||||
|
backgroundColor: colors.pageBg, |
||||
|
}, |
||||
|
header: { |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'space-between', |
||||
|
paddingHorizontal: 8, |
||||
|
height: 48, |
||||
|
backgroundColor: colors.white, |
||||
|
borderBottomWidth: 1, |
||||
|
borderBottomColor: colors.borderLight, |
||||
|
}, |
||||
|
headerBtn: { |
||||
|
width: 40, |
||||
|
height: 40, |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
}, |
||||
|
headerTitle: { |
||||
|
fontSize: 17, |
||||
|
fontWeight: '700', |
||||
|
color: colors.textPrimary, |
||||
|
}, |
||||
|
scroll: { |
||||
|
flex: 1, |
||||
|
}, |
||||
|
content: { |
||||
|
padding: 12, |
||||
|
paddingBottom: 24, |
||||
|
}, |
||||
|
headCard: { |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
padding: 16, |
||||
|
borderRadius: 12, |
||||
|
backgroundColor: colors.white, |
||||
|
marginBottom: 12, |
||||
|
}, |
||||
|
headBody: { |
||||
|
flex: 1, |
||||
|
marginLeft: 12, |
||||
|
}, |
||||
|
name: { |
||||
|
fontSize: 16, |
||||
|
fontWeight: '700', |
||||
|
color: colors.textPrimary, |
||||
|
}, |
||||
|
desc: { |
||||
|
marginTop: 2, |
||||
|
fontSize: 12, |
||||
|
color: colors.textSecondary, |
||||
|
}, |
||||
|
contentCard: { |
||||
|
padding: 16, |
||||
|
borderRadius: 12, |
||||
|
backgroundColor: colors.white, |
||||
|
borderWidth: 1, |
||||
|
borderColor: colors.borderLight, |
||||
|
}, |
||||
|
contentText: { |
||||
|
fontSize: 14, |
||||
|
lineHeight: 22, |
||||
|
color: colors.textPrimary, |
||||
|
}, |
||||
|
tip: { |
||||
|
marginTop: 12, |
||||
|
fontSize: 12, |
||||
|
color: colors.textHint, |
||||
|
lineHeight: 18, |
||||
|
}, |
||||
|
}); |
||||
Loading…
Reference in new issue