feat: 添加相机和相册权限请求功能

- 在 AI 体态评估页面中集成相机和相册权限请求逻辑
- 更新 app.json 和 Info.plist,添加相应的权限说明
- 修改布局以支持照片上传功能,用户可上传正面、侧面和背面照片
- 更新 package.json 和 package-lock.json,添加 expo-image-picker 依赖
This commit is contained in:
richarjiang
2025-08-12 17:30:26 +08:00
parent e84ad0857c
commit 2fac3f899c
9 changed files with 705 additions and 365 deletions

View File

@@ -4,9 +4,13 @@ import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import { IconSymbol } from '@/components/ui/IconSymbol';
import { Colors } from '@/constants/Colors';
import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() {
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
const colorTokens = Colors[theme];
const pathname = usePathname();
return (
@@ -19,9 +23,9 @@ export default function TabLayout() {
return {
headerShown: false,
tabBarActiveTintColor: '#192126',
tabBarActiveTintColor: colorTokens.tabIconSelected,
tabBarButton: (props) => {
const { children, onPress } = props;
const { onPress } = props;
const handlePress = (event: any) => {
if (process.env.EXPO_OS === 'ios') {
@@ -30,9 +34,28 @@ export default function TabLayout() {
onPress && onPress(event);
};
// 基于 routeName 设置图标与标题,避免 tabBarIcon 的包装导致文字裁剪
const getIconAndTitle = () => {
switch (routeName) {
case 'index':
return { icon: 'house.fill', title: '首页' } as const;
case 'explore':
return { icon: 'paperplane.fill', title: '探索' } as const;
case 'personal':
return { icon: 'person.fill', title: '个人' } as const;
default:
return { icon: 'circle', title: '' } as const;
}
};
const { icon, title } = getIconAndTitle();
const activeContentColor = colorTokens.onPrimary;
const inactiveContentColor = colorTokens.tabIconDefault;
return (
<TouchableOpacity
onPress={handlePress}
accessibilityRole="button"
style={{
flex: 1,
alignItems: 'center',
@@ -41,12 +64,32 @@ export default function TabLayout() {
marginHorizontal: 6,
marginVertical: 10,
borderRadius: 25,
backgroundColor: isSelected ? '#BBF246' : 'transparent',
paddingHorizontal: isSelected ? 16 : 8,
backgroundColor: isSelected ? colorTokens.tabBarActiveBackground : 'transparent',
paddingHorizontal: isSelected ? 16 : 10,
paddingVertical: 8,
}}
>
{children}
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol
size={22}
name={icon as any}
color={isSelected ? activeContentColor : inactiveContentColor}
/>
{isSelected && !!title && (
<Text
style={{
color: activeContentColor,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
}}
// 选中态下不限制行数,避免大屏布局下被裁剪成省略号
numberOfLines={0 as any}
>
{title}
</Text>
)}
</View>
</TouchableOpacity>
);
},
@@ -55,7 +98,7 @@ export default function TabLayout() {
bottom: TAB_BAR_BOTTOM_OFFSET,
height: TAB_BAR_HEIGHT,
borderRadius: 34,
backgroundColor: '#192126',
backgroundColor: colorTokens.tabBarBackground,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,