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 191 192 193 194 195 | 4x 4x 4x 1x | import { Modal, Pressable, StyleSheet } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { Text, XStack, YStack } from 'tamagui';
import { Button } from '@/src/design-system/components';
import { IconSymbol } from '@/src/components/ui/IconSymbol';
import { Colors } from '@/src/constants/theme';
import { useThemeAwareColorScheme } from '@/src/hooks/useThemeAwareColorScheme';
interface CameraPermissionModalProps {
visible: boolean;
onRequestPermission: () => void;
onClose?: () => void;
}
/**
* 相机权限请求模态框
* 以弹窗形式请求相机权限,提供更好的用户体验
*/
export function CameraPermissionModal({
visible,
onRequestPermission,
onClose,
}: CameraPermissionModalProps) {
const colorScheme = useThemeAwareColorScheme();
const _colors = Colors[colorScheme];
return (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={onClose}
statusBarTranslucent
>
<Pressable style={styles.overlay} onPress={onClose}>
<LinearGradient
colors={['rgba(0, 0, 0, 0.6)', 'rgba(0, 0, 0, 0.7)', 'rgba(0, 0, 0, 0.6)']}
style={styles.gradient}
>
{/* 模态框内容 */}
<Pressable onPress={(e) => e.stopPropagation()}>
<YStack
backgroundColor="white"
borderRadius="$6"
padding="$6"
margin="$4"
maxWidth={400}
alignSelf="center"
alignItems="center"
gap="$4"
>
{/* 相机图标 */}
<YStack
width={80}
height={80}
borderRadius="$12"
backgroundColor="#FFF5ED"
alignItems="center"
justifyContent="center"
borderWidth={3}
borderColor="#FFE4D1"
>
<IconSymbol name="camera.fill" size={40} color="#FEBE98" />
</YStack>
{/* 标题 */}
<Text fontSize={22} fontWeight="900" color="#111827" textAlign="center">
需要相机权限
</Text>
{/* 说明文字 */}
<Text
fontSize={15}
color="#6B7280"
textAlign="center"
lineHeight={22}
fontWeight="500"
>
为了扫描猫粮成分表,需要访问您的相机。我们不会存储或上传您的照片。
</Text>
{/* 权限说明列表 */}
<YStack
width="100%"
backgroundColor="#F9FAFB"
borderRadius="$4"
padding="$3"
gap="$2.5"
>
<XStack alignItems="center" gap="$2.5">
<YStack
width={20}
height={20}
borderRadius={10}
backgroundColor="#10B981"
alignItems="center"
justifyContent="center"
>
<Text fontSize={14} color="white" fontWeight="900">
✓
</Text>
</YStack>
<Text fontSize={13} color="#4B5563" fontWeight="600" flex={1}>
拍摄猫粮成分表
</Text>
</XStack>
<XStack alignItems="center" gap="$2.5">
<YStack
width={20}
height={20}
borderRadius={10}
backgroundColor="#10B981"
alignItems="center"
justifyContent="center"
>
<Text fontSize={14} color="white" fontWeight="900">
✓
</Text>
</YStack>
<Text fontSize={13} color="#4B5563" fontWeight="600" flex={1}>
扫描条形码快速识别
</Text>
</XStack>
<XStack alignItems="center" gap="$2.5">
<YStack
width={20}
height={20}
borderRadius={10}
backgroundColor="#10B981"
alignItems="center"
justifyContent="center"
>
<Text fontSize={14} color="white" fontWeight="900">
✓
</Text>
</YStack>
<Text fontSize={13} color="#4B5563" fontWeight="600" flex={1}>
离线处理,保护隐私
</Text>
</XStack>
</YStack>
{/* 按钮组 */}
<YStack width="100%" gap="$3" marginTop="$2">
{/* 授予权限按钮 */}
<Button
size="$5"
backgroundColor="#FEBE98"
color="white"
borderRadius="$4"
borderWidth={2}
borderColor="#FCA574"
onPress={onRequestPermission}
fontWeight="800"
fontSize={16}
pressStyle={{ scale: 0.98, backgroundColor: '#FCA574' }}
height={56}
>
授予相机权限
</Button>
{/* 取消按钮(可选) */}
{onClose && (
<Button
size="$4"
backgroundColor="transparent"
color="#6B7280"
onPress={onClose}
fontWeight="600"
fontSize={14}
pressStyle={{ opacity: 0.7 }}
>
稍后再说
</Button>
)}
</YStack>
</YStack>
</Pressable>
</LinearGradient>
</Pressable>
</Modal>
);
}
const styles = StyleSheet.create({
overlay: {
flex: 1,
},
gradient: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
|