feat: 新增饮水记录功能,支持快速添加饮水量和用户偏好设置

This commit is contained in:
richarjiang
2025-09-02 17:12:38 +08:00
parent 85a3c742df
commit ac748dc339
5 changed files with 612 additions and 210 deletions

View File

@@ -1,4 +1,5 @@
import { useWaterDataByDate } from '@/hooks/useWaterData';
import { getQuickWaterAmount } from '@/utils/userPreferences';
import dayjs from 'dayjs';
import React, { useEffect, useMemo, useState } from 'react';
import {
@@ -10,6 +11,7 @@ import {
ViewStyle
} from 'react-native';
import AddWaterModal from './AddWaterModal';
import { AnimatedNumber } from './AnimatedNumber';
interface WaterIntakeCardProps {
style?: ViewStyle;
@@ -22,6 +24,7 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
}) => {
const { waterStats, dailyWaterGoal, waterRecords, addWaterRecord } = useWaterDataByDate(selectedDate);
const [isModalVisible, setIsModalVisible] = useState(false);
const [quickWaterAmount, setQuickWaterAmount] = useState(150); // 默认值,将从用户偏好中加载
// 计算当前饮水量和目标
const currentIntake = waterStats?.totalAmount || 0;
@@ -64,9 +67,23 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
}));
}, [waterRecords]);
// 获取当前小时 - 只有当选中的是今天时才显示当前小时
// 判断是否是今天
const isToday = selectedDate === dayjs().format('YYYY-MM-DD') || !selectedDate;
const currentHour = isToday ? new Date().getHours() : -1; // 如果不是今天,设为-1表示没有当前小时
// 加载用户偏好的快速添加饮水默认值
useEffect(() => {
const loadQuickWaterAmount = async () => {
try {
const amount = await getQuickWaterAmount();
setQuickWaterAmount(amount);
} catch (error) {
console.error('加载快速添加饮水默认值失败:', error);
// 保持默认值 250ml
}
};
loadQuickWaterAmount();
}, []);
// 触发柱体动画
useEffect(() => {
@@ -96,8 +113,8 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
// 处理添加喝水 - 右上角按钮直接添加
const handleQuickAddWater = async () => {
// 默认添加250ml水
const waterAmount = 250;
// 使用用户配置的快速添加饮水量
const waterAmount = quickWaterAmount;
// 如果有选中日期,则为该日期添加记录;否则为今天添加记录
const recordedAt = selectedDate ? dayjs(selectedDate).toISOString() : dayjs().toISOString();
await addWaterRecord(waterAmount, recordedAt);
@@ -109,8 +126,16 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
};
// 处理关闭弹窗
const handleCloseModal = () => {
const handleCloseModal = async () => {
setIsModalVisible(false);
// 弹窗关闭后重新加载快速添加默认值,以防用户修改了设置
try {
const amount = await getQuickWaterAmount();
setQuickWaterAmount(amount);
} catch (error) {
console.error('刷新快速添加默认值失败:', error);
}
};
return (
@@ -123,9 +148,11 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
{/* 标题和加号按钮 */}
<View style={styles.header}>
<Text style={styles.title}></Text>
<TouchableOpacity style={styles.addButton} onPress={handleQuickAddWater}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
{isToday && (
<TouchableOpacity style={styles.addButton} onPress={handleQuickAddWater}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
)}
</View>
{/* 柱状图 */}
@@ -133,9 +160,8 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
<View style={styles.chartWrapper}>
<View style={styles.chartArea}>
{chartData.map((data, index) => {
// 判断是否是当前小时或者有活动的小时
// 判断是否有活动的小时
const isActive = data.amount > 0;
const isCurrent = isToday && index <= currentHour;
// 动画变换高度从0到目标高度
const animatedHeight = animatedValues[index].interpolate({
@@ -184,22 +210,21 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
{/* 饮水量显示 */}
<View style={styles.statsContainer}>
<Text style={styles.currentIntake}>
{currentIntake !== null ? `${currentIntake}ml` : '——'}
</Text>
{currentIntake !== null ? (
<AnimatedNumber
value={currentIntake}
style={styles.currentIntake}
format={(value) => `${Math.round(value)}ml`}
resetToken={selectedDate}
/>
) : (
<Text style={styles.currentIntake}></Text>
)}
<Text style={styles.targetIntake}>
/ {targetIntake}ml
</Text>
</View>
{/* 完成率显示 */}
{waterStats && (
<View style={styles.completionContainer}>
<Text style={styles.completionText}>
{Math.round(waterStats.completionRate)}%
</Text>
</View>
)}
</TouchableOpacity>
{/* 配置饮水弹窗 */}
@@ -233,6 +258,7 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 4,
minHeight: 22,
},
title: {
fontSize: 14,
@@ -298,15 +324,6 @@ const styles = StyleSheet.create({
color: '#6B7280',
marginLeft: 4,
},
completionContainer: {
alignItems: 'flex-start',
marginTop: 2,
},
completionText: {
fontSize: 12,
color: '#10B981',
fontWeight: '500',
},
});
export default WaterIntakeCard;