feat: 新增饮水记录功能,支持快速添加饮水量和用户偏好设置
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user