feat:支持身体围度数据展示
This commit is contained in:
189
components/statistic/CircumferenceCard.tsx
Normal file
189
components/statistic/CircumferenceCard.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import { FloatingSelectionModal, SelectionItem } from '@/components/ui/FloatingSelectionModal';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||
import { selectUserProfile, updateUserBodyMeasurements } from '@/store/userSlice';
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
interface CircumferenceCardProps {
|
||||
style?: any;
|
||||
}
|
||||
|
||||
const CircumferenceCard: React.FC<CircumferenceCardProps> = ({ style }) => {
|
||||
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: '胸围',
|
||||
value: userProfile?.chestCircumference,
|
||||
},
|
||||
{
|
||||
key: 'waistCircumference',
|
||||
label: '腰围',
|
||||
value: userProfile?.waistCircumference,
|
||||
},
|
||||
{
|
||||
key: 'upperHipCircumference',
|
||||
label: '上臀围',
|
||||
value: userProfile?.upperHipCircumference,
|
||||
},
|
||||
{
|
||||
key: 'armCircumference',
|
||||
label: '臂围',
|
||||
value: userProfile?.armCircumference,
|
||||
},
|
||||
{
|
||||
key: 'thighCircumference',
|
||||
label: '大腿围',
|
||||
value: userProfile?.thighCircumference,
|
||||
},
|
||||
{
|
||||
key: 'calfCircumference',
|
||||
label: '小腿围',
|
||||
value: userProfile?.calfCircumference,
|
||||
},
|
||||
];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
setSelectedMeasurement({
|
||||
key: measurement.key,
|
||||
label: measurement.label,
|
||||
currentValue: measurement.value,
|
||||
});
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleUpdateMeasurement = (value: string | number) => {
|
||||
if (!selectedMeasurement) return;
|
||||
|
||||
const updateData = {
|
||||
[selectedMeasurement.key]: Number(value),
|
||||
};
|
||||
|
||||
dispatch(updateUserBodyMeasurements(updateData));
|
||||
setModalVisible(false);
|
||||
setSelectedMeasurement(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
<Text style={styles.title}>围度 (cm)</Text>
|
||||
|
||||
<View style={styles.measurementsContainer}>
|
||||
{measurements.map((measurement, index) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={styles.measurementItem}
|
||||
onPress={() => 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 ? `设置${selectedMeasurement.label}` : '设置围度'}
|
||||
items={circumferenceOptions}
|
||||
selectedValue={selectedMeasurement?.currentValue}
|
||||
onValueChange={() => { }} // Real-time update not needed
|
||||
onConfirm={handleUpdateMeasurement}
|
||||
confirmButtonText="确认"
|
||||
pickerHeight={180}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
measurementsContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
flexWrap: 'nowrap',
|
||||
},
|
||||
measurementItem: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
label: {
|
||||
fontSize: 12,
|
||||
color: '#888',
|
||||
marginBottom: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
valueContainer: {
|
||||
backgroundColor: '#F5F5F7',
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 6,
|
||||
minWidth: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
value: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#192126',
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default CircumferenceCard;
|
||||
Reference in New Issue
Block a user