Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | import { memo, useState } from 'react';
import { ScrollView, TouchableOpacity } from 'react-native';
import { Avatar, Spinner, Text, XStack, YStack } from 'tamagui';
import { IconSymbol } from '@/src/components/ui/IconSymbol';
import { Colors } from '@/src/constants/theme';
import { useThemeAwareColorScheme } from '@/src/hooks/useThemeAwareColorScheme';
import type { Pet } from '@/src/schemas/pet.schema';
import { PetInfoPanel } from '../PetInfoPanel';
/**
* 宠物 Tab 组件的 Props
*/
interface PetsTabProps {
/** 宠物列表 */
pets: Pet[];
/** 是否正在加载 */
isLoading: boolean;
/** 添加宠物回调 */
onAddPet: () => void;
/** 删除宠物回调 */
onDeletePet?: (petId: number) => Promise<void>;
}
/**
* 宠物 Tab 组件
*
* 功能:
* - 显示宠物头像列表
* - 点击头像查看宠物详情
* - 添加新宠物
*
* @component
*/
export const PetsTab = memo(function PetsTab({
pets,
isLoading,
onAddPet,
onDeletePet,
}: PetsTabProps) {
const colorScheme = useThemeAwareColorScheme();
const colors = Colors[colorScheme];
const [selectedPet, setSelectedPet] = useState<Pet | null>(pets[0] || null);
// 自动选择第一个宠物
if (!selectedPet && pets.length > 0) {
setSelectedPet(pets[0]);
}
if (isLoading) {
return (
<YStack flex={1} alignItems="center" justifyContent="center" padding="$6">
<Spinner size="large" color="#FEBE98" />
<Text fontSize={14} color={colors.icon} marginTop="$3">
加载中...
</Text>
</YStack>
);
}
if (pets.length === 0) {
return (
<YStack flex={1} alignItems="center" justifyContent="center" padding="$6" gap="$4">
<YStack
width={100}
height={100}
borderRadius="$12"
backgroundColor="$gray2"
alignItems="center"
justifyContent="center"
>
<IconSymbol name="pawprint.fill" size={50} color={colors.icon + '60'} />
</YStack>
<Text fontSize={16} fontWeight="600" color={colors.text}>
还没有宠物
</Text>
<Text fontSize={14} color={colors.icon} textAlign="center">
点击下方按钮添加你的第一只宠物
</Text>
<TouchableOpacity onPress={onAddPet} activeOpacity={0.8}>
<YStack
paddingHorizontal="$6"
paddingVertical="$3"
backgroundColor="#FEBE98"
borderRadius="$4"
>
<XStack gap="$2" alignItems="center">
<IconSymbol name="plus.circle.fill" size={20} color="white" />
<Text fontSize={15} fontWeight="600" color="white">
添加宠物
</Text>
</XStack>
</YStack>
</TouchableOpacity>
</YStack>
);
}
return (
<ScrollView style={{ flex: 1 }} showsVerticalScrollIndicator={false}>
<YStack width="100%" alignItems="center" paddingVertical="$4" gap="$4">
{/* 宠物头像列表 */}
<YStack width="90%" gap="$3">
<XStack justifyContent="space-between" alignItems="center">
<Text fontSize={16} fontWeight="700" color={colors.text}>
我的宠物
</Text>
<TouchableOpacity onPress={onAddPet} activeOpacity={0.7}>
<XStack
gap="$2"
alignItems="center"
paddingHorizontal="$3"
paddingVertical="$2"
backgroundColor="#FEBE98"
borderRadius="$3"
>
<IconSymbol name="plus.circle.fill" size={16} color="white" />
<Text fontSize={13} fontWeight="600" color="white">
添加
</Text>
</XStack>
</TouchableOpacity>
</XStack>
{/* 宠物头像水平滑动列表 */}
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
paddingRight: 16,
gap: 12,
}}
>
{pets.map((pet) => {
const isSelected = selectedPet?.id === pet.id;
return (
<TouchableOpacity
key={pet.id}
onPress={() => setSelectedPet(pet)}
activeOpacity={0.7}
>
<YStack
width={80}
alignItems="center"
gap="$2"
padding="$2"
borderRadius="$3"
backgroundColor={isSelected ? '#FEF3E8' : 'transparent'}
>
<Avatar
circular
size={64}
borderWidth={isSelected ? 3 : 0}
borderColor={isSelected ? '#FEBE98' : 'transparent'}
>
{pet.photo_url ? (
<Avatar.Image src={pet.photo_url} />
) : (
<Avatar.Fallback
backgroundColor="$orange3"
justifyContent="center"
alignItems="center"
>
<Text fontSize={32}>🐱</Text>
</Avatar.Fallback>
)}
</Avatar>
<Text
fontSize={12}
fontWeight={isSelected ? '600' : '400'}
color={isSelected ? '#D97706' : colors.text}
numberOfLines={1}
textAlign="center"
>
{pet.name}
</Text>
</YStack>
</TouchableOpacity>
);
})}
</ScrollView>
</YStack>
{/* 选中宠物的详情面板 */}
{selectedPet && <PetInfoPanel pet={selectedPet} onDelete={onDeletePet} />}
</YStack>
</ScrollView>
);
});
|