Files
frontend/src/screens/profile/VerificationSettingsScreen.tsx

498 lines
14 KiB
TypeScript
Raw Normal View History

/**
*
*
*/
import React, { useEffect, useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
ScrollView,
Image,
Alert,
ActivityIndicator,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useRouter } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { useAppColors, type AppColors } from '../../theme';
import {
useVerificationStore,
useVerificationStatus,
useVerificationLoading,
} from '../../stores/verificationStore';
import { showPrompt } from '../../services/promptService';
import * as hrefs from '../../navigation/hrefs';
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',
};
const STATUS_CONFIG = {
not_submitted: {
title: '未认证',
description: '完成身份认证,解锁更多专属功能',
icon: 'account-question-outline',
color: '#999',
},
pending: {
title: '审核中',
description: '您的认证申请正在审核中,请耐心等待',
icon: 'clock-outline',
color: '#FFA500',
},
approved: {
title: '已认证',
description: '您已完成身份认证',
icon: 'check-decagram',
color: '#4CAF50',
},
rejected: {
title: '认证失败',
description: '您的认证申请被拒绝,可以重新提交',
icon: 'close-circle-outline',
color: '#F44336',
},
};
const IDENTITY_TEXT: Record<string, string> = {
general: '普通用户',
student: '在校学生',
teacher: '教职工',
staff: '工作人员',
alumni: '校友',
external: '外部人员',
};
export const VerificationSettingsScreen: React.FC = () => {
const colors = useAppColors();
const styles = createStyles(colors);
const router = useRouter();
const status = useVerificationStatus();
const loading = useVerificationLoading();
const { fetchStatus, reset } = useVerificationStore();
useEffect(() => {
fetchStatus();
return () => {
reset();
};
}, []);
const handleStartVerification = () => {
router.push('/auth/verification-guide');
};
const handleViewRecords = () => {
showPrompt({
title: '认证记录',
message: '认证记录功能即将上线',
type: 'info',
});
};
if (loading && !status) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={THEME_COLORS.primary} />
</View>
</SafeAreaView>
);
}
const statusConfig = STATUS_CONFIG[status?.verification_status || 'not_submitted'];
const identityText = IDENTITY_TEXT[status?.identity || 'general'];
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity
style={styles.backButton}
onPress={() => router.back()}
activeOpacity={0.8}
>
<MaterialCommunityIcons
name="arrow-left"
size={24}
color={colors.text?.primary || '#333'}
/>
</TouchableOpacity>
<Text style={styles.headerTitle}></Text>
<View style={styles.headerRight} />
</View>
<ScrollView
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* 状态卡片 */}
<View style={styles.statusCard}>
<View style={[styles.statusIconContainer, { backgroundColor: statusConfig.color + '20' }]}>
<MaterialCommunityIcons
name={statusConfig.icon as any}
size={48}
color={statusConfig.color}
/>
</View>
<Text style={styles.statusTitle}>{statusConfig.title}</Text>
<Text style={styles.statusDescription}>{statusConfig.description}</Text>
{status?.verification_status === 'approved' && (
<View style={styles.identityBadge}>
<MaterialCommunityIcons name="check-circle" size={16} color={THEME_COLORS.primary} />
<Text style={styles.identityText}>{identityText}</Text>
</View>
)}
{status?.latest_record?.reject_reason && (
<View style={styles.rejectReasonContainer}>
<Text style={styles.rejectReasonLabel}></Text>
<Text style={styles.rejectReasonText}>
{status.latest_record.reject_reason}
</Text>
</View>
)}
</View>
{/* 认证优势 */}
{status?.verification_status !== 'approved' && (
<View style={styles.benefitsSection}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.benefitsList}>
<View style={styles.benefitItem}>
<View style={styles.benefitIconContainer}>
<MaterialCommunityIcons
name="verified"
size={24}
color={THEME_COLORS.primary}
/>
</View>
<View style={styles.benefitContent}>
<Text style={styles.benefitTitle}></Text>
<Text style={styles.benefitDescription}>
</Text>
</View>
</View>
<View style={styles.benefitItem}>
<View style={styles.benefitIconContainer}>
<MaterialCommunityIcons
name="lock-open-variant"
size={24}
color={THEME_COLORS.primary}
/>
</View>
<View style={styles.benefitContent}>
<Text style={styles.benefitTitle}></Text>
<Text style={styles.benefitDescription}>
访
</Text>
</View>
</View>
<View style={styles.benefitItem}>
<View style={styles.benefitIconContainer}>
<MaterialCommunityIcons
name="account-group"
size={24}
color={THEME_COLORS.primary}
/>
</View>
<View style={styles.benefitContent}>
<Text style={styles.benefitTitle}></Text>
<Text style={styles.benefitDescription}>
</Text>
</View>
</View>
</View>
</View>
)}
{/* 操作按钮 */}
<View style={styles.actionSection}>
{(status?.verification_status === 'not_submitted' ||
status?.verification_status === 'rejected') && (
<TouchableOpacity
style={styles.primaryButton}
onPress={handleStartVerification}
activeOpacity={0.9}
>
<MaterialCommunityIcons name="shield-check-outline" size={20} color="#FFF" />
<Text style={styles.primaryButtonText}>
{status?.verification_status === 'rejected' ? '重新认证' : '开始认证'}
</Text>
</TouchableOpacity>
)}
{status?.verification_status === 'pending' && (
<TouchableOpacity
style={styles.secondaryButton}
onPress={handleViewRecords}
activeOpacity={0.8}
>
<MaterialCommunityIcons name="file-document-outline" size={20} color={THEME_COLORS.primary} />
<Text style={styles.secondaryButtonText}></Text>
</TouchableOpacity>
)}
{status?.verification_status === 'approved' && (
<>
<View style={styles.verifiedInfo}>
<View style={styles.verifiedInfoRow}>
<Text style={styles.verifiedInfoLabel}></Text>
<Text style={styles.verifiedInfoValue}>{identityText}</Text>
</View>
{status.latest_record?.real_name && (
<View style={styles.verifiedInfoRow}>
<Text style={styles.verifiedInfoLabel}></Text>
<Text style={styles.verifiedInfoValue}>
{status.latest_record.real_name}
</Text>
</View>
)}
{status.latest_record?.department && (
<View style={styles.verifiedInfoRow}>
<Text style={styles.verifiedInfoLabel}>/</Text>
<Text style={styles.verifiedInfoValue}>
{status.latest_record.department}
</Text>
</View>
)}
</View>
</>
)}
</View>
{/* 帮助信息 */}
<View style={styles.helpSection}>
<Text style={styles.helpTitle}></Text>
<Text style={styles.helpText}>
</Text>
</View>
</ScrollView>
</SafeAreaView>
);
};
function createStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider || '#E0E0E0',
},
backButton: {
width: 40,
height: 40,
justifyContent: 'center',
alignItems: 'center',
},
headerTitle: {
fontSize: 18,
fontWeight: '600',
color: colors.text?.primary || '#333',
},
headerRight: {
width: 40,
},
scrollContent: {
flexGrow: 1,
padding: 24,
},
statusCard: {
alignItems: 'center',
paddingVertical: 32,
paddingHorizontal: 24,
backgroundColor: '#F9F9F9',
borderRadius: 16,
marginBottom: 24,
},
statusIconContainer: {
width: 96,
height: 96,
borderRadius: 48,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 16,
},
statusTitle: {
fontSize: 24,
fontWeight: '700',
color: colors.text?.primary || '#333',
marginBottom: 8,
},
statusDescription: {
fontSize: 15,
color: colors.text?.secondary || '#666',
textAlign: 'center',
lineHeight: 22,
},
identityBadge: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFF5F0',
borderRadius: 20,
paddingVertical: 8,
paddingHorizontal: 16,
marginTop: 16,
},
identityText: {
fontSize: 14,
fontWeight: '600',
color: THEME_COLORS.primary,
marginLeft: 6,
},
rejectReasonContainer: {
marginTop: 16,
padding: 12,
backgroundColor: '#FFF3F3',
borderRadius: 8,
width: '100%',
},
rejectReasonLabel: {
fontSize: 13,
fontWeight: '600',
color: '#F44336',
marginBottom: 4,
},
rejectReasonText: {
fontSize: 13,
color: '#D32F2F',
lineHeight: 18,
},
benefitsSection: {
marginBottom: 24,
},
sectionTitle: {
fontSize: 17,
fontWeight: '600',
color: colors.text?.primary || '#333',
marginBottom: 16,
},
benefitsList: {
gap: 12,
},
benefitItem: {
flexDirection: 'row',
alignItems: 'flex-start',
backgroundColor: '#F9F9F9',
borderRadius: 12,
padding: 16,
},
benefitIconContainer: {
width: 48,
height: 48,
borderRadius: 12,
backgroundColor: '#FFF5F0',
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
},
benefitContent: {
flex: 1,
},
benefitTitle: {
fontSize: 15,
fontWeight: '600',
color: colors.text?.primary || '#333',
marginBottom: 4,
},
benefitDescription: {
fontSize: 13,
color: colors.text?.secondary || '#666',
lineHeight: 18,
},
actionSection: {
marginBottom: 24,
},
primaryButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
height: 56,
borderRadius: 14,
backgroundColor: THEME_COLORS.primary,
},
primaryButtonText: {
fontSize: 17,
fontWeight: '600',
color: '#FFF',
marginLeft: 8,
},
secondaryButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
height: 56,
borderRadius: 14,
backgroundColor: '#FFF5F0',
borderWidth: 1,
borderColor: THEME_COLORS.primary,
},
secondaryButtonText: {
fontSize: 17,
fontWeight: '600',
color: THEME_COLORS.primary,
marginLeft: 8,
},
verifiedInfo: {
backgroundColor: '#F9F9F9',
borderRadius: 12,
padding: 16,
},
verifiedInfoRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 8,
},
verifiedInfoLabel: {
fontSize: 15,
color: colors.text?.secondary || '#666',
},
verifiedInfoValue: {
fontSize: 15,
fontWeight: '500',
color: colors.text?.primary || '#333',
},
helpSection: {
alignItems: 'center',
paddingVertical: 16,
},
helpTitle: {
fontSize: 15,
fontWeight: '600',
color: colors.text?.primary || '#333',
marginBottom: 8,
},
helpText: {
fontSize: 13,
color: colors.text?.hint || '#999',
textAlign: 'center',
lineHeight: 18,
},
});
}
export default VerificationSettingsScreen;