/** * Avatar 头像组件 * 支持图片URL、本地图片、首字母显示、在线状态徽章 * 使用 expo-image 实现内存+磁盘双级缓存,头像秒加载 */ import React from 'react'; import { View, TouchableOpacity, StyleSheet, ViewStyle, } from 'react-native'; import { Image as ExpoImage } from 'expo-image'; import { colors, borderRadius } from '../../theme'; import Text from './Text'; type AvatarSource = string | { uri: string } | number | null; interface AvatarProps { source?: AvatarSource; size?: number; // 默认40 name?: string; // 用于显示首字母 onPress?: () => void; showBadge?: boolean; badgeColor?: string; style?: ViewStyle; } const Avatar: React.FC = ({ source, size = 40, name, onPress, showBadge = false, badgeColor = colors.success.main, style, }) => { // 获取首字母 const getInitial = (): string => { if (!name) return '?'; const firstChar = name.charAt(0).toUpperCase(); // 中文字符 if (/[\u4e00-\u9fa5]/.test(firstChar)) { return firstChar; } return firstChar; }; // 渲染头像内容 const renderAvatarContent = () => { // 如果有图片源 if (source) { const imageSource = typeof source === 'string' ? { uri: source } : source; return ( ); } // 显示首字母 const fontSize = size / 2; return ( {getInitial()} ); }; const avatarContainer = ( {renderAvatarContent()} {showBadge && ( )} ); if (onPress) { return ( {avatarContainer} ); } return avatarContainer; }; const styles = StyleSheet.create({ container: { position: 'relative', }, image: { backgroundColor: colors.background.disabled, }, placeholder: { backgroundColor: colors.primary.main, justifyContent: 'center', alignItems: 'center', }, badge: { position: 'absolute', borderWidth: 2, borderColor: colors.background.paper, }, }); export default Avatar;