245 lines
7.2 KiB
TypeScript
245 lines
7.2 KiB
TypeScript
import { FloatingSelectionModal, SelectionItem } from '@/components/ui/FloatingSelectionModal';
|
||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||
import { selectUserProfile, updateUserBodyMeasurements, UserProfile } from '@/store/userSlice';
|
||
import { router } from 'expo-router';
|
||
import React, { useState } from 'react';
|
||
import { useTranslation } from 'react-i18next';
|
||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||
|
||
interface CircumferenceCardProps {
|
||
style?: any;
|
||
}
|
||
|
||
const CircumferenceCard: React.FC<CircumferenceCardProps> = ({ style }) => {
|
||
const { t } = useTranslation();
|
||
const dispatch = useAppDispatch();
|
||
const userProfile = useAppSelector(selectUserProfile);
|
||
|
||
|
||
console.log('userProfile', userProfile);
|
||
|
||
|
||
const { ensureLoggedIn } = useAuthGuard()
|
||
|
||
const [modalVisible, setModalVisible] = useState(false);
|
||
const [selectedMeasurement, setSelectedMeasurement] = useState<{
|
||
key: string;
|
||
label: string;
|
||
currentValue?: number;
|
||
} | null>(null);
|
||
|
||
const measurements = [
|
||
{
|
||
key: 'chestCircumference',
|
||
label: t('statistics.components.circumference.measurements.chest'),
|
||
value: userProfile?.chestCircumference,
|
||
},
|
||
{
|
||
key: 'waistCircumference',
|
||
label: t('statistics.components.circumference.measurements.waist'),
|
||
value: userProfile?.waistCircumference,
|
||
},
|
||
{
|
||
key: 'upperHipCircumference',
|
||
label: t('statistics.components.circumference.measurements.hip'),
|
||
value: userProfile?.upperHipCircumference,
|
||
},
|
||
{
|
||
key: 'armCircumference',
|
||
label: t('statistics.components.circumference.measurements.arm'),
|
||
value: userProfile?.armCircumference,
|
||
},
|
||
{
|
||
key: 'thighCircumference',
|
||
label: t('statistics.components.circumference.measurements.thigh'),
|
||
value: userProfile?.thighCircumference,
|
||
},
|
||
{
|
||
key: 'calfCircumference',
|
||
label: t('statistics.components.circumference.measurements.calf'),
|
||
value: userProfile?.calfCircumference,
|
||
},
|
||
];
|
||
|
||
// 根据不同围度类型获取合理的默认值
|
||
const getDefaultCircumferenceValue = (measurementKey: string, userProfile?: UserProfile): number => {
|
||
// 如果用户已有该围度数据,直接使用
|
||
const existingValue = userProfile?.[measurementKey as keyof UserProfile] as number;
|
||
if (existingValue) {
|
||
return existingValue;
|
||
}
|
||
|
||
// 根据性别设置合理的默认值
|
||
const isMale = userProfile?.gender === 'male';
|
||
|
||
switch (measurementKey) {
|
||
case 'chestCircumference':
|
||
// 胸围:男性 85-110cm,女性 75-95cm
|
||
return isMale ? 95 : 80;
|
||
case 'waistCircumference':
|
||
// 腰围:男性 70-90cm,女性 60-80cm
|
||
return isMale ? 80 : 70;
|
||
case 'upperHipCircumference':
|
||
// 上臀围:
|
||
return 30;
|
||
case 'armCircumference':
|
||
// 臂围:男性 25-35cm,女性 20-30cm
|
||
return isMale ? 30 : 25;
|
||
case 'thighCircumference':
|
||
// 大腿围:男性 45-60cm,女性 40-55cm
|
||
return isMale ? 50 : 45;
|
||
case 'calfCircumference':
|
||
// 小腿围:男性 30-40cm,女性 25-35cm
|
||
return isMale ? 35 : 30;
|
||
default:
|
||
return 70; // 默认70cm
|
||
}
|
||
};
|
||
|
||
// Generate circumference options (30-150 cm)
|
||
const circumferenceOptions: SelectionItem[] = Array.from({ length: 121 }, (_, i) => {
|
||
const value = i + 30;
|
||
return {
|
||
label: `${value} cm`,
|
||
value: value,
|
||
};
|
||
});
|
||
|
||
const handleMeasurementPress = async (measurement: typeof measurements[0]) => {
|
||
const isLoggedIn = await ensureLoggedIn();
|
||
if (!isLoggedIn) {
|
||
// 如果未登录,用户会被重定向到登录页面
|
||
return;
|
||
}
|
||
|
||
// 使用智能默认值,如果用户已有数据则使用现有数据,否则使用基于性别的合理默认值
|
||
const defaultValue = getDefaultCircumferenceValue(measurement.key, userProfile);
|
||
|
||
setSelectedMeasurement({
|
||
key: measurement.key,
|
||
label: measurement.label,
|
||
currentValue: measurement.value || defaultValue,
|
||
});
|
||
setModalVisible(true);
|
||
};
|
||
|
||
const handleUpdateMeasurement = (value: string | number) => {
|
||
if (!selectedMeasurement) return;
|
||
|
||
const updateData = {
|
||
[selectedMeasurement.key]: Number(value),
|
||
};
|
||
|
||
dispatch(updateUserBodyMeasurements(updateData));
|
||
setModalVisible(false);
|
||
setSelectedMeasurement(null);
|
||
};
|
||
|
||
// 处理整个卡片点击,跳转到详情页
|
||
const handleCardPress = () => {
|
||
router.push('/circumference-detail');
|
||
};
|
||
|
||
return (
|
||
<TouchableOpacity
|
||
style={[styles.container, style]}
|
||
onPress={handleCardPress}
|
||
activeOpacity={0.8}
|
||
>
|
||
<Text style={styles.title}>{t('statistics.components.circumference.title')}</Text>
|
||
|
||
<View style={styles.measurementsContainer}>
|
||
{measurements.map((measurement, index) => (
|
||
<TouchableOpacity
|
||
key={index}
|
||
style={styles.measurementItem}
|
||
onPress={(e) => {
|
||
e.stopPropagation(); // 阻止事件冒泡
|
||
handleMeasurementPress(measurement);
|
||
}}
|
||
activeOpacity={0.7}
|
||
>
|
||
<Text style={styles.label}>{measurement.label}</Text>
|
||
<View style={styles.valueContainer}>
|
||
<Text style={styles.value}>
|
||
{measurement.value ? measurement.value.toString() : '--'}
|
||
</Text>
|
||
</View>
|
||
</TouchableOpacity>
|
||
))}
|
||
</View>
|
||
|
||
<FloatingSelectionModal
|
||
visible={modalVisible}
|
||
onClose={() => {
|
||
setModalVisible(false);
|
||
setSelectedMeasurement(null);
|
||
}}
|
||
title={selectedMeasurement ? t('statistics.components.circumference.setTitle', { label: selectedMeasurement.label }) : t('statistics.components.circumference.title')}
|
||
items={circumferenceOptions}
|
||
selectedValue={selectedMeasurement?.currentValue}
|
||
onValueChange={() => { }} // Real-time update not needed
|
||
onConfirm={handleUpdateMeasurement}
|
||
confirmButtonText={t('statistics.components.circumference.confirm')}
|
||
pickerHeight={180}
|
||
/>
|
||
</TouchableOpacity>
|
||
);
|
||
};
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 16,
|
||
padding: 20,
|
||
shadowColor: '#000',
|
||
shadowOffset: {
|
||
width: 0,
|
||
height: 4,
|
||
},
|
||
shadowOpacity: 0.12,
|
||
shadowRadius: 12,
|
||
elevation: 6,
|
||
},
|
||
title: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
color: '#192126',
|
||
marginBottom: 16,
|
||
fontFamily: 'AliBold',
|
||
},
|
||
measurementsContainer: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
flexWrap: 'nowrap',
|
||
},
|
||
measurementItem: {
|
||
alignItems: 'center',
|
||
},
|
||
label: {
|
||
fontSize: 12,
|
||
color: '#888',
|
||
marginBottom: 8,
|
||
textAlign: 'center',
|
||
fontFamily: 'AliRegular',
|
||
},
|
||
valueContainer: {
|
||
backgroundColor: '#F5F5F7',
|
||
borderRadius: 10,
|
||
paddingHorizontal: 8,
|
||
paddingVertical: 6,
|
||
minWidth: 20,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
},
|
||
value: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
color: '#192126',
|
||
textAlign: 'center',
|
||
fontFamily: 'AliBold',
|
||
},
|
||
});
|
||
|
||
export default CircumferenceCard; |