Files
frontend/src/components/common/Avatar.tsx

147 lines
3.1 KiB
TypeScript
Raw Normal View History

/**
* 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<AvatarProps> = ({
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 (
<ExpoImage
source={imageSource}
style={[
styles.image,
{
width: size,
height: size,
borderRadius: size / 2,
},
]}
contentFit="cover"
cachePolicy="memory"
transition={150}
/>
);
}
// 显示首字母
const fontSize = size / 2;
return (
<View
style={[
styles.placeholder,
{
width: size,
height: size,
borderRadius: size / 2,
},
]}
>
<Text color={colors.text.inverse} style={{ fontSize, lineHeight: fontSize * 1.2 }}>
{getInitial()}
</Text>
</View>
);
};
const avatarContainer = (
<View style={[styles.container, style]}>
{renderAvatarContent()}
{showBadge && (
<View
style={[
styles.badge,
{
backgroundColor: badgeColor,
width: size * 0.3,
height: size * 0.3,
borderRadius: (size * 0.3) / 2,
right: 0,
bottom: 0,
},
]}
/>
)}
</View>
);
if (onPress) {
return (
<TouchableOpacity onPress={onPress} activeOpacity={0.7}>
{avatarContainer}
</TouchableOpacity>
);
}
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;