✨ feat: 完整应用重构 - 优化界面架构与用户体验
主要变更: - 重构应用界面:优化首页、登录、认证流程 - 新增用户档案模块:包含完整的档案管理组件 - 优化路由结构:重新组织页面布局和导航 - 改进API集成:新增活动、内容分类等API模块 - 删除冗余组件:清理不必要的文件和依赖 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
184
components/profile/profile-identity.tsx
Normal file
184
components/profile/profile-identity.tsx
Normal file
@@ -0,0 +1,184 @@
|
||||
import React from 'react';
|
||||
import { View, TouchableOpacity, Image, type ImageSourcePropType } from 'react-native';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
|
||||
import { ThemedText } from '@/components/themed-text';
|
||||
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||
import { deriveInitials, type IdentityStat } from '@/utils/profile-data';
|
||||
|
||||
export function ProfileIdentity({
|
||||
displayName,
|
||||
avatarSource,
|
||||
avatarSize,
|
||||
stats,
|
||||
onEdit,
|
||||
}: {
|
||||
displayName: string;
|
||||
avatarSource?: ImageSourcePropType;
|
||||
avatarSize: number;
|
||||
stats: IdentityStat[];
|
||||
onEdit: () => void;
|
||||
}) {
|
||||
const colorScheme = useColorScheme();
|
||||
const palette = colorScheme === 'dark' ? darkPalette : lightPalette;
|
||||
|
||||
return (
|
||||
<View style={styles.identityRow}>
|
||||
<Avatar
|
||||
palette={palette}
|
||||
size={avatarSize}
|
||||
source={avatarSource}
|
||||
fallback={displayName}
|
||||
/>
|
||||
<View style={styles.identityDetails}>
|
||||
<View style={styles.nameRow}>
|
||||
<ThemedText numberOfLines={1} style={[styles.username, { color: palette.textPrimary }]}>
|
||||
{displayName}
|
||||
</ThemedText>
|
||||
<TouchableOpacity
|
||||
onPress={onEdit}
|
||||
activeOpacity={0.85}
|
||||
style={[
|
||||
styles.editButton,
|
||||
{
|
||||
borderColor: palette.border,
|
||||
backgroundColor: palette.surface,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<MaterialIcons name="edit" size={16} color={palette.textPrimary} style={styles.editIcon} />
|
||||
<ThemedText style={[styles.editLabel, { color: palette.textPrimary }]}>Edit</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={styles.statRow}>
|
||||
{stats.map(stat => (
|
||||
<StatItem key={stat.key} palette={palette} label={stat.label} value={stat.value} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function Avatar({
|
||||
palette,
|
||||
size,
|
||||
source,
|
||||
fallback,
|
||||
}: {
|
||||
palette: any;
|
||||
size: number;
|
||||
source?: ImageSourcePropType;
|
||||
fallback: string;
|
||||
}) {
|
||||
const initials = deriveInitials(fallback);
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.avatarShell,
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
backgroundColor: palette.avatarBackdrop,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{source ? (
|
||||
<Image source={source} style={{ width: size, height: size, borderRadius: size / 2 }} resizeMode="cover" />
|
||||
) : (
|
||||
<ThemedText style={[styles.avatarInitials, { color: palette.textPrimary }]}>{initials}</ThemedText>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function StatItem({ palette, label, value }: { palette: any; label: string; value: number }) {
|
||||
return (
|
||||
<View style={styles.statItem}>
|
||||
<ThemedText style={[styles.statValue, { color: palette.textPrimary }]}>{value}</ThemedText>
|
||||
<ThemedText style={[styles.statLabel, { color: palette.textSecondary }]}>{label}</ThemedText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const darkPalette = {
|
||||
textPrimary: '#F6F7FA',
|
||||
textSecondary: '#8E9098',
|
||||
avatarBackdrop: '#1F2026',
|
||||
surface: '#121216',
|
||||
border: '#1D1E24',
|
||||
};
|
||||
|
||||
const lightPalette = {
|
||||
textPrimary: '#0F1320',
|
||||
textSecondary: '#5E6474',
|
||||
avatarBackdrop: '#D5D8E2',
|
||||
surface: '#FFFFFF',
|
||||
border: '#E2E5ED',
|
||||
};
|
||||
|
||||
const styles = {
|
||||
identityRow: {
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center' as const,
|
||||
marginBottom: 24,
|
||||
},
|
||||
avatarShell: {
|
||||
alignItems: 'center' as const,
|
||||
justifyContent: 'center' as const,
|
||||
},
|
||||
avatarInitials: {
|
||||
fontSize: 28,
|
||||
fontWeight: '700' as const,
|
||||
},
|
||||
identityDetails: {
|
||||
flex: 1,
|
||||
marginLeft: 18,
|
||||
},
|
||||
nameRow: {
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center' as const,
|
||||
marginBottom: 10,
|
||||
},
|
||||
username: {
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: '700' as const,
|
||||
marginRight: 12,
|
||||
},
|
||||
editButton: {
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center' as const,
|
||||
borderRadius: 999,
|
||||
borderWidth: 1,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
editIcon: {
|
||||
marginRight: 6,
|
||||
},
|
||||
editLabel: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
},
|
||||
statRow: {
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center' as const,
|
||||
},
|
||||
statItem: {
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'baseline' as const,
|
||||
marginRight: 18,
|
||||
},
|
||||
statValue: {
|
||||
fontSize: 15,
|
||||
fontWeight: '700' as const,
|
||||
marginRight: 4,
|
||||
},
|
||||
statLabel: {
|
||||
fontSize: 13,
|
||||
fontWeight: '500' as const,
|
||||
textTransform: 'lowercase' as const,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user