feat(ui): 统一应用主题色为天空蓝并优化渐变背景
将应用主色调从 '#BBF246' 更改为 '#87CEEB'(天空蓝),并更新所有相关组件和页面中的颜色引用。同时为多个页面添加统一的渐变背景,提升视觉效果和用户体验。新增压力分析模态框组件,并优化压力计组件的交互与显示逻辑。更新应用图标和启动图资源。
This commit is contained in:
@@ -1,20 +1,21 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||
import {
|
||||
BMI_CATEGORIES,
|
||||
canCalculateBMI,
|
||||
getBMIResult,
|
||||
type BMIResult
|
||||
BMI_CATEGORIES,
|
||||
canCalculateBMI,
|
||||
getBMIResult,
|
||||
type BMIResult
|
||||
} from '@/utils/bmi';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
Modal,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
Dimensions,
|
||||
Modal,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Toast from 'react-native-toast-message';
|
||||
|
||||
@@ -62,7 +63,7 @@ export function BMICard({ weight, height, style, compact = false }: BMICardProps
|
||||
if (!canCalculate) {
|
||||
// 缺少数据的情况
|
||||
return (
|
||||
<TouchableOpacity
|
||||
<TouchableOpacity
|
||||
style={[styles.incompleteContent, compact && styles.compactIncompleteContent]}
|
||||
onPress={handleShowInfoModal}
|
||||
activeOpacity={0.8}
|
||||
@@ -118,8 +119,8 @@ export function BMICard({ weight, height, style, compact = false }: BMICardProps
|
||||
|
||||
// 有完整数据的情况
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.completeContent, { backgroundColor: bmiResult?.backgroundColor }, compact && styles.compactCompleteContent]}
|
||||
<TouchableOpacity
|
||||
style={styles.completeContent}
|
||||
onPress={handleShowInfoModal}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
@@ -193,7 +194,7 @@ export function BMICard({ weight, height, style, compact = false }: BMICardProps
|
||||
style={styles.modalBackdrop}
|
||||
onPress={handleHideInfoModal}
|
||||
>
|
||||
<Pressable
|
||||
<Pressable
|
||||
style={styles.modalContainer}
|
||||
onPress={(e) => e.stopPropagation()}
|
||||
>
|
||||
@@ -234,7 +235,7 @@ export function BMICard({ weight, height, style, compact = false }: BMICardProps
|
||||
<View style={styles.categoriesGrid}>
|
||||
{BMI_CATEGORIES.map((category, index) => {
|
||||
const colors = index === 0 ? { bg: '#FEF3C7', text: '#B45309' } :
|
||||
index === 1 ? { bg: '#E8F5E8', text: '#2D5016' } :
|
||||
index === 1 ? { bg: '#E8F5E8', text: Colors.light.accentGreenDark } :
|
||||
index === 2 ? { bg: '#FEF3C7', text: '#B45309' } :
|
||||
{ bg: '#FEE2E2', text: '#B91C1C' };
|
||||
|
||||
@@ -289,6 +290,7 @@ const styles = StyleSheet.create({
|
||||
padding: 18,
|
||||
marginBottom: 16,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#FFFFFF',
|
||||
},
|
||||
cardHeader: {
|
||||
flexDirection: 'row',
|
||||
@@ -320,7 +322,7 @@ const styles = StyleSheet.create({
|
||||
|
||||
// 缺少数据时的样式
|
||||
incompleteContent: {
|
||||
minHeight: 120,
|
||||
minHeight: 80,
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 22,
|
||||
padding: 18,
|
||||
@@ -345,7 +347,7 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#F3F4F6',
|
||||
backgroundColor: '#ffffff',
|
||||
borderRadius: 12,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
@@ -359,7 +361,7 @@ const styles = StyleSheet.create({
|
||||
|
||||
// 有完整数据时的样式
|
||||
completeContent: {
|
||||
minHeight: 120,
|
||||
minHeight: 80,
|
||||
borderRadius: 22,
|
||||
padding: 18,
|
||||
margin: -18,
|
||||
@@ -398,12 +400,12 @@ const styles = StyleSheet.create({
|
||||
|
||||
// 紧凑模式样式
|
||||
compactIncompleteContent: {
|
||||
minHeight: 110,
|
||||
minHeight: 80,
|
||||
padding: 14,
|
||||
margin: -14,
|
||||
},
|
||||
compactCompleteContent: {
|
||||
minHeight: 110,
|
||||
minHeight: 80,
|
||||
padding: 14,
|
||||
margin: -14,
|
||||
},
|
||||
@@ -427,7 +429,7 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
},
|
||||
compactBMIValue: {
|
||||
fontSize: 28,
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
marginBottom: 4,
|
||||
},
|
||||
@@ -491,13 +493,13 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
|
||||
|
||||
// 内容区域样式
|
||||
modalContent: {
|
||||
paddingHorizontal: 20,
|
||||
paddingBottom: 24,
|
||||
},
|
||||
|
||||
|
||||
// 介绍部分
|
||||
introSection: {
|
||||
marginBottom: 20,
|
||||
@@ -522,7 +524,7 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
|
||||
|
||||
// 分类部分
|
||||
categoriesSection: {
|
||||
marginBottom: 18,
|
||||
@@ -567,7 +569,7 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '500',
|
||||
opacity: 0.9,
|
||||
},
|
||||
|
||||
|
||||
// 健康提示
|
||||
healthTips: {
|
||||
backgroundColor: '#F9FAFB',
|
||||
@@ -593,7 +595,7 @@ const styles = StyleSheet.create({
|
||||
color: '#374151',
|
||||
lineHeight: 18,
|
||||
},
|
||||
|
||||
|
||||
// 免责声明紧凑版
|
||||
disclaimerCompact: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Animated, Easing, LayoutChangeEvent, StyleSheet, Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
@@ -16,7 +17,7 @@ function ProgressBarImpl({
|
||||
height = 18,
|
||||
style,
|
||||
trackColor = '#EDEDED',
|
||||
fillColor = '#BBF246',
|
||||
fillColor = Colors.light.accentGreen,
|
||||
animated = true,
|
||||
showLabel = true,
|
||||
}: ProgressBarProps) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import React, { useMemo } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import Svg, * as SvgLib from 'react-native-svg';
|
||||
@@ -102,7 +103,7 @@ export function RadarChart({
|
||||
<Svg width={actualSize} height={actualSize}>
|
||||
<SvgLib.Defs>
|
||||
<SvgLib.LinearGradient id="radarFill" x1="0" y1="0" x2="1" y2="1">
|
||||
<SvgLib.Stop offset="0%" stopColor="#BBF246" stopOpacity={0.32} />
|
||||
<SvgLib.Stop offset="0%" stopColor={Colors.light.accentGreen} stopOpacity={0.32} />
|
||||
<SvgLib.Stop offset="100%" stopColor="#59C6FF" stopOpacity={0.28} />
|
||||
</SvgLib.LinearGradient>
|
||||
</SvgLib.Defs>
|
||||
|
||||
267
components/StressAnalysisModal.tsx
Normal file
267
components/StressAnalysisModal.tsx
Normal file
@@ -0,0 +1,267 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
interface StressAnalysisModalProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
hrvValue: number;
|
||||
updateTime: Date;
|
||||
}
|
||||
|
||||
export function StressAnalysisModal({ visible, onClose, hrvValue, updateTime }: StressAnalysisModalProps) {
|
||||
const colorScheme = useColorScheme();
|
||||
const colors = Colors[colorScheme ?? 'light'];
|
||||
|
||||
// 模拟30天HRV数据
|
||||
const hrvData = {
|
||||
goodEvents: { percentage: 26, count: 53, range: '>80毫秒' },
|
||||
energetic: { percentage: 47, count: 97, range: '43-80毫秒' },
|
||||
stressed: { percentage: 27, count: 56, range: '<43毫秒' },
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
animationType="slide"
|
||||
presentationStyle="pageSheet"
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={[colors.backgroundGradientStart, colors.backgroundGradientEnd]}
|
||||
style={styles.modalContainer}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 0, y: 1 }}
|
||||
>
|
||||
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
|
||||
{/* 标题 */}
|
||||
<Text style={styles.title}>压力情况分析</Text>
|
||||
|
||||
|
||||
{/* 最近30天HRV情况 */}
|
||||
<Text style={styles.sectionTitle}>最近30天HRV情况</Text>
|
||||
|
||||
{/* 彩色横条图 */}
|
||||
<View style={styles.chartContainer}>
|
||||
<View style={styles.colorBar}>
|
||||
<LinearGradient
|
||||
colors={['#10B981', '#3B82F6', '#F59E0B']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.gradientBar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.legend}>
|
||||
<View style={styles.legendItem}>
|
||||
<View style={[styles.legendDot, { backgroundColor: '#10B981' }]} />
|
||||
<Text style={styles.legendText}>好事发生</Text>
|
||||
</View>
|
||||
<View style={styles.legendItem}>
|
||||
<View style={[styles.legendDot, { backgroundColor: '#3B82F6' }]} />
|
||||
<Text style={styles.legendText}>活力满满</Text>
|
||||
</View>
|
||||
<View style={styles.legendItem}>
|
||||
<View style={[styles.legendDot, { backgroundColor: '#F59E0B' }]} />
|
||||
<Text style={styles.legendText}>鸭梨山大</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 数据统计卡片 */}
|
||||
<View style={styles.statsCard}>
|
||||
{/* 好事发生 & 活力满满 */}
|
||||
<View style={styles.statsRow}>
|
||||
<View style={styles.statItem}>
|
||||
<Text style={[styles.statTitle, { color: '#10B981' }]}>好事发生</Text>
|
||||
<Text style={styles.statPercentage}>{hrvData.goodEvents.percentage}%</Text>
|
||||
<View style={styles.statDetails}>
|
||||
<Text style={styles.statRange}>❤️ {hrvData.goodEvents.range}</Text>
|
||||
</View>
|
||||
<Text style={styles.statCount}>{hrvData.goodEvents.count}次</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.statItem}>
|
||||
<Text style={[styles.statTitle, { color: '#3B82F6' }]}>活力满满</Text>
|
||||
<Text style={styles.statPercentage}>{hrvData.energetic.percentage}%</Text>
|
||||
<View style={styles.statDetails}>
|
||||
<Text style={styles.statRange}>❤️ {hrvData.energetic.range}</Text>
|
||||
</View>
|
||||
<Text style={styles.statCount}>{hrvData.energetic.count}次</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 鸭梨山大 */}
|
||||
<View style={styles.statItem}>
|
||||
<Text style={[styles.statTitle, { color: '#F59E0B' }]}>鸭梨山大</Text>
|
||||
<Text style={styles.statPercentage}>{hrvData.stressed.percentage}%</Text>
|
||||
<View style={styles.statDetails}>
|
||||
<Text style={styles.statRange}>❤️ {hrvData.stressed.range}</Text>
|
||||
</View>
|
||||
<Text style={styles.statCount}>{hrvData.stressed.count}次</Text>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{/* 底部继续按钮 */}
|
||||
<View style={styles.bottomContainer}>
|
||||
<TouchableOpacity style={styles.continueButton} onPress={onClose}>
|
||||
<View style={styles.buttonBackground}>
|
||||
<Text style={styles.buttonText}>继续</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.homeIndicator} />
|
||||
</View>
|
||||
</LinearGradient>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modalContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
color: '#111827',
|
||||
textAlign: 'center',
|
||||
marginTop: 20,
|
||||
marginBottom: 32,
|
||||
},
|
||||
|
||||
sectionTitle: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
color: '#111827',
|
||||
marginBottom: 20,
|
||||
},
|
||||
chartContainer: {
|
||||
marginBottom: 32,
|
||||
},
|
||||
colorBar: {
|
||||
height: 16,
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 16,
|
||||
},
|
||||
gradientBar: {
|
||||
flex: 1,
|
||||
},
|
||||
legend: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
},
|
||||
legendItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
legendDot: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: 6,
|
||||
marginRight: 6,
|
||||
},
|
||||
legendText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#374151',
|
||||
},
|
||||
statsCard: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
marginBottom: 32,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 8,
|
||||
elevation: 2,
|
||||
},
|
||||
statsRow: {
|
||||
flexDirection: 'row',
|
||||
gap: 20,
|
||||
marginBottom: 24,
|
||||
},
|
||||
statItem: {
|
||||
flex: 1,
|
||||
},
|
||||
statTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
marginBottom: 8,
|
||||
},
|
||||
statPercentage: {
|
||||
fontSize: 36,
|
||||
fontWeight: '800',
|
||||
color: '#111827',
|
||||
marginBottom: 4,
|
||||
},
|
||||
statDetails: {
|
||||
marginBottom: 4,
|
||||
},
|
||||
statRange: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#DC2626',
|
||||
backgroundColor: '#FEE2E2',
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
borderRadius: 10,
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
statCount: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: '#6B7280',
|
||||
},
|
||||
bottomContainer: {
|
||||
paddingHorizontal: 20,
|
||||
paddingBottom: 34,
|
||||
},
|
||||
continueButton: {
|
||||
borderRadius: 25,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 8,
|
||||
},
|
||||
buttonGradient: {
|
||||
paddingVertical: 18,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
buttonBackground: {
|
||||
backgroundColor: Colors.light.accentGreen, // 应用主色调
|
||||
paddingVertical: 18,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
color: '#192126', // 主色调上的文字颜色
|
||||
},
|
||||
homeIndicator: {
|
||||
width: 134,
|
||||
height: 5,
|
||||
backgroundColor: '#000',
|
||||
borderRadius: 3,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
});
|
||||
@@ -1,25 +1,27 @@
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
interface StressMeterProps {
|
||||
value: number;
|
||||
value: number | null;
|
||||
updateTime?: Date;
|
||||
style?: any;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
export function StressMeter({ value, updateTime, style }: StressMeterProps) {
|
||||
|
||||
// 计算进度条位置(0-100%)
|
||||
// HRV值范围:30-110ms,对应进度条0-100%
|
||||
const progressPercentage = Math.min(100, Math.max(0, ((value - 30) / 80) * 100));
|
||||
|
||||
// 根据HRV值计算状态
|
||||
const getHrvStatus = () => {
|
||||
if (value >= 70) {
|
||||
export function StressMeter({ value, updateTime, style, onPress }: StressMeterProps) {
|
||||
// 将HRV值转换为压力指数(0-100)
|
||||
// HRV值范围:30-110ms,映射到压力指数100-0
|
||||
// HRV值越高,压力越小;HRV值越低,压力越大
|
||||
const stressIndex = value ? Math.round(Math.min(100, Math.max(0, 100 - ((value - 30) / 80) * 100))) : null;
|
||||
// 根据压力指数计算状态
|
||||
const getStressStatus = () => {
|
||||
if (stressIndex === null) {
|
||||
return '未知';
|
||||
} else if (stressIndex <= 30) {
|
||||
return '放松';
|
||||
} else if (value >= 50) {
|
||||
} else if (stressIndex <= 70) {
|
||||
return '正常';
|
||||
} else {
|
||||
return '紧张';
|
||||
@@ -28,13 +30,14 @@ export function StressMeter({ value, updateTime, style }: StressMeterProps) {
|
||||
|
||||
// 根据状态获取表情
|
||||
const getStatusEmoji = () => {
|
||||
// 当HRV值为0时,不展示表情
|
||||
if (value === 0) {
|
||||
// 当HRV值为null或0时,不展示表情
|
||||
if (value === null || value === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const status = getHrvStatus();
|
||||
|
||||
const status = getStressStatus();
|
||||
switch (status) {
|
||||
case '未知': return '';
|
||||
case '放松': return '😌';
|
||||
case '正常': return '😊';
|
||||
case '紧张': return '😰';
|
||||
@@ -42,24 +45,18 @@ export function StressMeter({ value, updateTime, style }: StressMeterProps) {
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化更新时间
|
||||
const formatUpdateTime = (date?: Date) => {
|
||||
if (!date) return '';
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
};
|
||||
// 计算进度条位置(0-100%)
|
||||
// 压力指数越高,进度条越满
|
||||
const progressPercentage = stressIndex === null ? 0 : stressIndex;
|
||||
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
{/* 渐变背景 */}
|
||||
<LinearGradient
|
||||
colors={['#F8F9FF', '#F0F4FF']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.gradientBackground}
|
||||
/>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.container, style]}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
|
||||
{/* 头部区域 */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.leftSection}>
|
||||
@@ -73,30 +70,32 @@ export function StressMeter({ value, updateTime, style }: StressMeterProps) {
|
||||
|
||||
{/* 数值显示区域 */}
|
||||
<View style={styles.valueSection}>
|
||||
<Text style={styles.value}>{value}</Text>
|
||||
<Text style={styles.unit}>毫秒</Text>
|
||||
<Text style={styles.value}>{stressIndex === null ? '--' : stressIndex}</Text>
|
||||
<Text style={styles.unit}>指数</Text>
|
||||
</View>
|
||||
|
||||
{/* 进度条区域 */}
|
||||
<View style={styles.progressContainer}>
|
||||
<View style={styles.progressTrack}>
|
||||
{/* 渐变背景进度条 */}
|
||||
<LinearGradient
|
||||
colors={['#FFD700', '#87CEEB', '#98FB98']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.gradientTrack}
|
||||
/>
|
||||
<View style={[styles.progressBar, { width: `${progressPercentage}%` }]}>
|
||||
<LinearGradient
|
||||
colors={['#10B981', '#FCD34D', '#F97316']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.gradientBar}
|
||||
/>
|
||||
</View>
|
||||
{/* 白色圆形指示器 */}
|
||||
<View style={[styles.indicator, { left: `${Math.max(0, Math.min(100, progressPercentage - 2))}%` }]} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 更新时间 */}
|
||||
{/* 更新时间
|
||||
{updateTime && (
|
||||
<Text style={styles.updateTime}>{formatUpdateTime(updateTime)}</Text>
|
||||
)}
|
||||
</View>
|
||||
)} */}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -181,7 +180,12 @@ const styles = StyleSheet.create({
|
||||
position: 'relative',
|
||||
overflow: 'visible',
|
||||
},
|
||||
gradientTrack: {
|
||||
progressBar: {
|
||||
height: '100%',
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
gradientBar: {
|
||||
height: '100%',
|
||||
borderRadius: 4,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { ROUTES } from '@/constants/Routes';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||
@@ -20,11 +21,6 @@ const CHART_WIDTH = CARD_WIDTH - 36; // 减去卡片内边距
|
||||
const CHART_HEIGHT = 100;
|
||||
const PADDING = 10;
|
||||
|
||||
type WeightHistoryItem = {
|
||||
weight: string;
|
||||
source: string;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export function WeightHistoryCard() {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -177,7 +173,7 @@ export function WeightHistoryCard() {
|
||||
<Ionicons
|
||||
name={showChart ? "chevron-up" : "chevron-down"}
|
||||
size={16}
|
||||
color="#BBF246"
|
||||
color={Colors.light.primary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
@@ -185,7 +181,7 @@ export function WeightHistoryCard() {
|
||||
onPress={navigateToCoach}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Ionicons name="add" size={16} color="#BBF246" />
|
||||
<Ionicons name="add" size={16} color={Colors.light.primary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
@@ -209,14 +205,6 @@ export function WeightHistoryCard() {
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.viewTrendButton}
|
||||
onPress={() => setShowChart(true)}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Ionicons name="trending-up" size={14} color="#BBF246" />
|
||||
<Text style={styles.viewTrendText}>查看趋势图</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -240,7 +228,7 @@ export function WeightHistoryCard() {
|
||||
{/* 折线 */}
|
||||
<Path
|
||||
d={singlePointPath}
|
||||
stroke="#BBF246"
|
||||
stroke={Colors.light.accentGreen}
|
||||
strokeWidth={3}
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
@@ -259,7 +247,7 @@ export function WeightHistoryCard() {
|
||||
cx={point.x}
|
||||
cy={point.y}
|
||||
r={isLastPoint ? 6 : 4}
|
||||
fill="#BBF246"
|
||||
fill={Colors.light.accentGreen}
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
@@ -271,7 +259,7 @@ export function WeightHistoryCard() {
|
||||
cy={point.y - 15}
|
||||
r={10}
|
||||
fill="rgba(255,255,255,0.9)"
|
||||
stroke="#BBF246"
|
||||
stroke={Colors.light.accentGreen}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<SvgText
|
||||
@@ -343,7 +331,6 @@ const styles = StyleSheet.create({
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#F0F8E0',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 10,
|
||||
@@ -363,7 +350,6 @@ const styles = StyleSheet.create({
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 14,
|
||||
backgroundColor: '#F0F8E0',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
@@ -371,7 +357,6 @@ const styles = StyleSheet.create({
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 14,
|
||||
backgroundColor: '#F0F8E0',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
@@ -394,7 +379,7 @@ const styles = StyleSheet.create({
|
||||
recordButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#BBF246',
|
||||
backgroundColor: Colors.light.accentGreen,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
borderRadius: 20,
|
||||
@@ -437,12 +422,11 @@ const styles = StyleSheet.create({
|
||||
marginTop: 8,
|
||||
},
|
||||
summaryInfo: {
|
||||
paddingVertical: 12,
|
||||
},
|
||||
summaryRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
marginBottom: 12,
|
||||
marginBottom: 6,
|
||||
},
|
||||
summaryItem: {
|
||||
alignItems: 'center',
|
||||
@@ -457,20 +441,4 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '700',
|
||||
color: '#192126',
|
||||
},
|
||||
viewTrendButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#F0F8E0',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 16,
|
||||
gap: 6,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
viewTrendText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
color: '#192126',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ export function HeaderBar({
|
||||
]}
|
||||
>
|
||||
{onBack ? (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={onBack} style={[styles.backButton, { backgroundColor: 'rgba(187,242,70,0.2)' }]}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={onBack} style={[styles.backButton, { backgroundColor: `${Colors.light.accentGreen}33` }]}>
|
||||
<Ionicons name="chevron-back" size={20} color={theme.onPrimary} />
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user