- 新增 i18n 翻译资源,覆盖睡眠、饮水、体重、锻炼、用药 AI 识别、步数、健身圆环、基础代谢及设置等核心模块 - 重构相关页面及组件(如 SleepDetail, WaterDetail, WorkoutHistory 等)使用 `useI18n` 钩子替换硬编码文本 - 升级 `utils/date` 工具库与 `DateSelector` 组件,支持基于语言环境的日期格式化与显示 - 完善登录页、注销流程及权限申请弹窗的双语提示信息 - 优化部分页面的 UI 细节与字体样式以适配多语言显示
181 lines
4.9 KiB
TypeScript
181 lines
4.9 KiB
TypeScript
import { Colors } from '@/constants/Colors';
|
||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||
import { useI18n } from '@/hooks/useI18n';
|
||
import { WeightHistoryItem } from '@/store/userSlice';
|
||
import { Ionicons } from '@expo/vector-icons';
|
||
import dayjs from 'dayjs';
|
||
import React, { useRef } from 'react';
|
||
import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||
import { Swipeable } from 'react-native-gesture-handler';
|
||
|
||
interface WeightRecordCardProps {
|
||
record: WeightHistoryItem;
|
||
onPress?: (record: WeightHistoryItem) => void;
|
||
onDelete?: (recordId: string) => void;
|
||
weightChange?: number;
|
||
}
|
||
|
||
export const WeightRecordCard: React.FC<WeightRecordCardProps> = ({
|
||
record,
|
||
onPress,
|
||
onDelete,
|
||
weightChange = 0
|
||
}) => {
|
||
const { t } = useI18n();
|
||
const swipeableRef = useRef<Swipeable>(null);
|
||
const colorScheme = useColorScheme();
|
||
const themeColors = Colors[colorScheme ?? 'light'];
|
||
|
||
// 处理删除操作
|
||
const handleDelete = () => {
|
||
Alert.alert(
|
||
t('weightRecords.card.deleteConfirmTitle'),
|
||
t('weightRecords.card.deleteConfirmMessage'),
|
||
[
|
||
{
|
||
text: t('weightRecords.card.cancelButton'),
|
||
style: 'cancel',
|
||
},
|
||
{
|
||
text: t('weightRecords.card.deleteButton'),
|
||
style: 'destructive',
|
||
onPress: () => {
|
||
const recordId = record.id || record.createdAt;
|
||
onDelete?.(recordId);
|
||
swipeableRef.current?.close();
|
||
},
|
||
},
|
||
]
|
||
);
|
||
};
|
||
|
||
// 渲染删除按钮
|
||
const renderRightActions = () => {
|
||
return (
|
||
<TouchableOpacity
|
||
style={styles.deleteButton}
|
||
onPress={handleDelete}
|
||
activeOpacity={0.8}
|
||
>
|
||
<Ionicons name="trash" size={20} color="#FFFFFF" />
|
||
<Text style={styles.deleteButtonText}>{t('weightRecords.card.deleteButton')}</Text>
|
||
</TouchableOpacity>
|
||
);
|
||
};
|
||
|
||
return (
|
||
<Swipeable
|
||
ref={swipeableRef}
|
||
renderRightActions={renderRightActions}
|
||
rightThreshold={40}
|
||
overshootRight={false}
|
||
>
|
||
<View
|
||
style={[styles.recordCard]}
|
||
>
|
||
<View style={styles.recordHeader}>
|
||
<Text style={[styles.recordDateTime, { color: themeColors.textSecondary }]}>
|
||
{dayjs(record.createdAt).format('MM[月]DD[日] HH:mm')}
|
||
</Text>
|
||
<TouchableOpacity
|
||
style={styles.recordEditButton}
|
||
onPress={() => onPress?.(record)}
|
||
>
|
||
<Ionicons name="create-outline" size={16} color="#FF9500" />
|
||
</TouchableOpacity>
|
||
</View>
|
||
<View style={styles.recordContent}>
|
||
<Text style={[styles.recordWeightLabel, { color: themeColors.textSecondary }]}>{t('weightRecords.card.weightLabel')}:</Text>
|
||
<Text style={[styles.recordWeightValue, { color: themeColors.text }]}>{record.weight}{t('weightRecords.modal.unit')}</Text>
|
||
{Math.abs(weightChange) > 0 && (
|
||
<View style={[
|
||
styles.weightChangeTag,
|
||
{ backgroundColor: weightChange < 0 ? '#E8F5E8' : '#FFF2E8' }
|
||
]}>
|
||
<Ionicons
|
||
name={weightChange < 0 ? "arrow-down" : "arrow-up"}
|
||
size={12}
|
||
color={weightChange < 0 ? Colors.light.accentGreen : '#FF9500'}
|
||
/>
|
||
<Text style={[
|
||
styles.weightChangeText,
|
||
{ color: weightChange < 0 ? Colors.light.accentGreen : '#FF9500' }
|
||
]}>
|
||
{Math.abs(weightChange).toFixed(1)}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</Swipeable>
|
||
);
|
||
};
|
||
|
||
const styles = StyleSheet.create({
|
||
recordCard: {
|
||
backgroundColor: '#ffffff',
|
||
borderRadius: 16,
|
||
padding: 20,
|
||
marginBottom: 12,
|
||
},
|
||
recordHeader: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
marginBottom: 12,
|
||
},
|
||
recordDateTime: {
|
||
fontSize: 14,
|
||
color: '#687076',
|
||
fontWeight: '500',
|
||
},
|
||
recordEditButton: {
|
||
padding: 6,
|
||
borderRadius: 8,
|
||
backgroundColor: 'rgba(255, 149, 0, 0.1)',
|
||
},
|
||
recordContent: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
},
|
||
recordWeightLabel: {
|
||
fontSize: 16,
|
||
color: '#687076',
|
||
fontWeight: '500',
|
||
},
|
||
recordWeightValue: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#192126',
|
||
marginLeft: 4,
|
||
flex: 1,
|
||
},
|
||
weightChangeTag: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
paddingHorizontal: 8,
|
||
paddingVertical: 4,
|
||
borderRadius: 12,
|
||
marginLeft: 12,
|
||
},
|
||
weightChangeText: {
|
||
fontSize: 12,
|
||
fontWeight: '600',
|
||
marginLeft: 2,
|
||
},
|
||
deleteButton: {
|
||
backgroundColor: '#EF4444',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
width: 80,
|
||
borderRadius: 16,
|
||
marginBottom: 12,
|
||
marginLeft: 8,
|
||
},
|
||
deleteButtonText: {
|
||
color: '#FFFFFF',
|
||
fontSize: 12,
|
||
fontWeight: '600',
|
||
marginTop: 4,
|
||
},
|
||
}); |