feat: 更新个人页面和活动热图组件

- 在个人页面中新增鱼干记录展示,优化用户界面
- 修改活动热图组件,增加信息弹窗,提供小鱼干使用说明
- 调整样式,提升整体视觉效果和用户体验
- 更新颜色常量,确保一致性
This commit is contained in:
richarjiang
2025-08-27 12:48:43 +08:00
parent 9bb924202f
commit aaa462d476
5 changed files with 125 additions and 21 deletions

View File

@@ -264,6 +264,16 @@ export default function PersonalScreen() {
> >
<UserHeader /> <UserHeader />
<StatsSection /> <StatsSection />
<View style={styles.fishRecordContainer}>
{/* <Image
source={{ uri: 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/icons/icon-profile-fish.png' }}
contentFit="cover"
style={{ width: 16, height: 16, marginLeft: 6 }}
transition={200}
cachePolicy="memory-disk"
/> */}
<Text style={styles.fishRecordText}></Text>
</View>
<ActivityHeatMap /> <ActivityHeatMap />
{menuSections.map((section, index) => ( {menuSections.map((section, index) => (
<MenuSection key={index} title={section.title} items={section.items} /> <MenuSection key={index} title={section.title} items={section.items} />
@@ -277,7 +287,7 @@ export default function PersonalScreen() {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#FAFAFA', backgroundColor: '#F0F9FF',
}, },
safeArea: { safeArea: {
flex: 1, flex: 1,
@@ -402,4 +412,16 @@ const styles = StyleSheet.create({
switch: { switch: {
transform: [{ scaleX: 0.8 }, { scaleY: 0.8 }], transform: [{ scaleX: 0.8 }, { scaleY: 0.8 }],
}, },
fishRecordContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
marginBottom: 10,
},
fishRecordText: {
fontSize: 16,
fontWeight: 'bold',
color: '#2C3E50',
marginLeft: 4,
},
}); });

View File

@@ -1,14 +1,17 @@
import { IconSymbol } from '@/components/ui/IconSymbol';
import { Colors } from '@/constants/Colors'; import { Colors } from '@/constants/Colors';
import { useAppSelector } from '@/hooks/redux'; import { useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme'; import { useColorScheme } from '@/hooks/useColorScheme';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import React, { useMemo } from 'react'; import React, { useMemo, useState } from 'react';
import { Dimensions, StyleSheet, Text, View } from 'react-native'; import { Dimensions, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import Popover from 'react-native-popover-view';
const ActivityHeatMap = () => { const ActivityHeatMap = () => {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
const colors = Colors[colorScheme ?? 'light']; const colors = Colors[colorScheme ?? 'light'];
const [showPopover, setShowPopover] = useState(false);
const activityData = useAppSelector(stat => stat.user.activityHistory); const activityData = useAppSelector(stat => stat.user.activityHistory);
@@ -63,8 +66,8 @@ const ActivityHeatMap = () => {
const getActivityColor = (level: number): string => { const getActivityColor = (level: number): string => {
switch (level) { switch (level) {
case 0: case 0:
// 无活动:使用主题适配的背景色 // 无活动:使用淡蓝色,更有活力
return colors.separator; return 'rgba(173, 216, 230, 0.3)'; // 淡蓝色,有活力但不突兀
case 1: case 1:
// 低活动:使用主题主色的浅色版本 // 低活动:使用主题主色的浅色版本
return 'rgba(122, 90, 248, 0.15)'; // 浅色模式下的浅紫色 return 'rgba(122, 90, 248, 0.15)'; // 浅色模式下的浅紫色
@@ -159,12 +162,53 @@ const ActivityHeatMap = () => {
<Text style={[styles.subtitle, { color: colors.textMuted }]}> <Text style={[styles.subtitle, { color: colors.textMuted }]}>
6 {activityStats.activeDays} 6 {activityStats.activeDays}
</Text> </Text>
<View style={[styles.statsBadge, { <View style={styles.rightSection}>
backgroundColor: 'rgba(122, 90, 248, 0.1)' <View style={[styles.statsBadge, {
}]}> backgroundColor: 'rgba(122, 90, 248, 0.1)'
<Text style={[styles.statsText, { color: colors.primary }]}> }]}>
{activityStats.activeRate}% <Text style={[styles.statsText, { color: colors.primary }]}>
</Text> {activityStats.activeRate}%
</Text>
</View>
<Popover
isVisible={showPopover}
onRequestClose={() => setShowPopover(false)}
from={(
<TouchableOpacity
style={styles.infoButton}
onPress={() => setShowPopover(true)}
>
<IconSymbol
name="info.circle"
size={16}
color={colors.textMuted}
/>
</TouchableOpacity>
)}
>
<View style={[styles.popoverContent, { backgroundColor: colors.card }]}>
<Text style={[styles.popoverTitle, { color: colors.text }]}>
</Text>
<Text style={[styles.popoverSubtitle, { color: colors.text }]}>
</Text>
<View style={styles.popoverList}>
<Text style={[styles.popoverItem, { color: colors.textMuted }]}>
1. +1
</Text>
<Text style={[styles.popoverItem, { color: colors.textMuted }]}>
2. +1
</Text>
<Text style={[styles.popoverItem, { color: colors.textMuted }]}>
3. +1
</Text>
<Text style={[styles.popoverItem, { color: colors.textMuted }]}>
4. +1
</Text>
</View>
</View>
</Popover>
</View> </View>
</View> </View>
@@ -263,6 +307,15 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
marginBottom: 6, marginBottom: 6,
}, },
rightSection: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
infoButton: {
padding: 4,
borderRadius: 8,
},
title: { title: {
fontSize: 18, fontSize: 18,
fontWeight: 'bold', fontWeight: 'bold',
@@ -332,6 +385,34 @@ const styles = StyleSheet.create({
borderRadius: 2.5, borderRadius: 2.5,
borderWidth: 0.5, borderWidth: 0.5,
}, },
popoverContent: {
padding: 16,
borderRadius: 12,
maxWidth: 280,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 5,
},
popoverTitle: {
fontSize: 16,
fontWeight: '600',
marginBottom: 12,
textAlign: 'center',
},
popoverSubtitle: {
fontSize: 14,
fontWeight: '600',
marginBottom: 8,
},
popoverList: {
gap: 6,
},
popoverItem: {
fontSize: 14,
lineHeight: 20,
},
}); });
export default ActivityHeatMap; export default ActivityHeatMap;

View File

@@ -122,8 +122,8 @@ const styles = StyleSheet.create({
gap: 8, gap: 8,
}, },
goalsIconButton: { goalsIconButton: {
width: 24, width: 18,
height: 24, height: 18,
}, },
title: { title: {
fontSize: 20, fontSize: 20,

View File

@@ -21,6 +21,7 @@ const MAPPING = {
'person.fill': 'person', 'person.fill': 'person',
'person.3.fill': 'people', 'person.3.fill': 'people',
'message.fill': 'message', 'message.fill': 'message',
'info.circle': 'info',
} as IconMapping; } as IconMapping;
/** /**

View File

@@ -100,7 +100,7 @@ export const Colors = {
textMuted: palette.gray[400], // 浅灰色用于静音文本 textMuted: palette.gray[400], // 浅灰色用于静音文本
background: palette.base.white, background: palette.base.white,
surface: palette.base.white, surface: palette.base.white,
card: palette.gray[25], // 最浅灰色用于卡片背景 card: '#ffffff', // 最浅灰色用于卡片背景
// 品牌与可交互主色 // 品牌与可交互主色
tint: tintColorLight, tint: tintColorLight,