- 更新 CLAUDE.md 文件,重构架构部分,增加认证和数据层的描述 - 在 GoalsScreen 中新增目标模板选择功能,支持用户选择和创建目标 - 在 CreateGoalModal 中添加初始数据支持,优化目标创建体验 - 新增 GoalTemplateModal 组件,提供目标模板选择界面 - 更新 NotificationHelpers,支持构建深度链接以便于导航 - 在 CoachScreen 中处理路由参数,增强用户交互体验 - 更新多个组件的样式和逻辑,提升整体用户体验 - 删除不再使用的中文回复规则文档
295 lines
7.1 KiB
TypeScript
295 lines
7.1 KiB
TypeScript
import { Colors } from '@/constants/Colors';
|
||
import { getTemplatesByCategory, goalCategories, GoalTemplate } from '@/constants/goalTemplates';
|
||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||
import React, { useState } from 'react';
|
||
import {
|
||
Dimensions,
|
||
Image,
|
||
Modal,
|
||
SafeAreaView,
|
||
ScrollView,
|
||
StatusBar,
|
||
StyleSheet,
|
||
Text,
|
||
TouchableOpacity,
|
||
View
|
||
} from 'react-native';
|
||
|
||
const { width: screenWidth } = Dimensions.get('window');
|
||
|
||
interface GoalTemplateModalProps {
|
||
visible: boolean;
|
||
onClose: () => void;
|
||
onSelectTemplate: (template: GoalTemplate) => void;
|
||
onCreateCustom: () => void;
|
||
}
|
||
|
||
export default function GoalTemplateModal({
|
||
visible,
|
||
onClose,
|
||
onSelectTemplate,
|
||
onCreateCustom
|
||
}: GoalTemplateModalProps) {
|
||
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
||
const colorTokens = Colors[theme];
|
||
|
||
const [selectedCategory, setSelectedCategory] = useState('recommended');
|
||
const currentTemplates = getTemplatesByCategory(selectedCategory);
|
||
|
||
// 渲染分类标签
|
||
const renderCategoryTab = (category: any) => {
|
||
const isSelected = selectedCategory === category.id;
|
||
return (
|
||
<TouchableOpacity
|
||
key={category.id}
|
||
style={[
|
||
styles.categoryTab,
|
||
isSelected && styles.categoryTabSelected
|
||
]}
|
||
onPress={() => setSelectedCategory(category.id)}
|
||
activeOpacity={0.8}
|
||
>
|
||
{category.isRecommended && (
|
||
<Image
|
||
source={require('@/assets/images/icons/icon-recommend.png')}
|
||
style={{ width: 24, height: 24 }}
|
||
/>
|
||
)}
|
||
<Text style={[
|
||
styles.categoryTabText,
|
||
isSelected && styles.categoryTabTextSelected
|
||
]}>
|
||
{category.title}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
);
|
||
};
|
||
|
||
// 渲染模板项
|
||
const renderTemplateItem = (template: GoalTemplate) => (
|
||
<TouchableOpacity
|
||
key={template.id}
|
||
style={[styles.templateItem]}
|
||
onPress={() => onSelectTemplate(template)}
|
||
activeOpacity={0.8}
|
||
>
|
||
<View style={[styles.templateIcon]}>
|
||
<MaterialIcons
|
||
name={template.icon as any}
|
||
size={24}
|
||
color={template.iconColor}
|
||
/>
|
||
</View>
|
||
<Text style={[styles.templateTitle]}>
|
||
{template.title}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
);
|
||
|
||
return (
|
||
<Modal
|
||
visible={visible}
|
||
animationType="slide"
|
||
presentationStyle="pageSheet"
|
||
onRequestClose={onClose}
|
||
>
|
||
<SafeAreaView style={styles.container}>
|
||
<StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
|
||
|
||
{/* 头部 */}
|
||
<View style={styles.header}>
|
||
<TouchableOpacity
|
||
onPress={onClose}
|
||
style={styles.closeButton}
|
||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||
>
|
||
<MaterialIcons name="keyboard-arrow-down" size={24} color="#374151" />
|
||
</TouchableOpacity>
|
||
|
||
<Text style={styles.title}>创建新目标</Text>
|
||
|
||
<View style={{ width: 24 }} />
|
||
</View>
|
||
|
||
<ScrollView
|
||
style={styles.content}
|
||
contentContainerStyle={styles.scrollContent}
|
||
showsVerticalScrollIndicator={false}
|
||
>
|
||
{/* 创建自定义目标 */}
|
||
<TouchableOpacity
|
||
style={styles.customGoalButton}
|
||
onPress={onCreateCustom}
|
||
activeOpacity={0.8}
|
||
>
|
||
<View style={styles.customGoalIcon}>
|
||
<MaterialIcons name="add" size={24} color="#7C3AED" />
|
||
</View>
|
||
<Text style={styles.customGoalText}>创建自定义目标</Text>
|
||
</TouchableOpacity>
|
||
|
||
{/* 分类选择器 */}
|
||
<View style={styles.categorySection}>
|
||
<ScrollView
|
||
horizontal
|
||
showsHorizontalScrollIndicator={false}
|
||
contentContainerStyle={styles.categoryScrollContent}
|
||
style={styles.categoryScrollView}
|
||
>
|
||
{goalCategories.map(renderCategoryTab)}
|
||
</ScrollView>
|
||
</View>
|
||
|
||
{/* 当前分类的模板 */}
|
||
<View style={styles.templatesSection}>
|
||
<View style={styles.templatesGrid}>
|
||
{currentTemplates.map(renderTemplateItem)}
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
</SafeAreaView>
|
||
</Modal>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
flex: 1,
|
||
backgroundColor: '#F9FAFB',
|
||
},
|
||
header: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
paddingHorizontal: 20,
|
||
paddingVertical: 16,
|
||
backgroundColor: '#FFFFFF',
|
||
borderBottomWidth: 1,
|
||
borderBottomColor: '#E5E7EB',
|
||
},
|
||
closeButton: {
|
||
padding: 4,
|
||
},
|
||
title: {
|
||
fontSize: 18,
|
||
fontWeight: '600',
|
||
color: '#111827',
|
||
textAlign: 'center',
|
||
},
|
||
content: {
|
||
flex: 1,
|
||
},
|
||
scrollContent: {
|
||
paddingBottom: 40,
|
||
},
|
||
customGoalButton: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
marginHorizontal: 20,
|
||
marginTop: 20,
|
||
padding: 16,
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 16,
|
||
borderWidth: 1,
|
||
borderColor: '#E5E7EB',
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 1 },
|
||
shadowOpacity: 0.05,
|
||
shadowRadius: 2,
|
||
elevation: 1,
|
||
},
|
||
customGoalIcon: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 20,
|
||
backgroundColor: '#F3E8FF',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
marginRight: 12,
|
||
},
|
||
customGoalText: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#374151',
|
||
},
|
||
categorySection: {
|
||
marginTop: 24,
|
||
},
|
||
categoryScrollView: {
|
||
paddingLeft: 20,
|
||
},
|
||
categoryScrollContent: {
|
||
paddingRight: 20,
|
||
},
|
||
categoryTab: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
paddingHorizontal: 16,
|
||
paddingVertical: 10,
|
||
marginRight: 12,
|
||
borderRadius: 20,
|
||
backgroundColor: '#F3F4F6',
|
||
},
|
||
categoryTabSelected: {
|
||
backgroundColor: '#7C3AED',
|
||
},
|
||
categoryTabText: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
color: '#374151',
|
||
},
|
||
categoryTabTextSelected: {
|
||
color: '#FFFFFF',
|
||
},
|
||
avatarGroup: {
|
||
flexDirection: 'row',
|
||
marginRight: 8,
|
||
},
|
||
avatar: {
|
||
width: 16,
|
||
height: 16,
|
||
borderRadius: 8,
|
||
borderWidth: 1,
|
||
borderColor: '#FFFFFF',
|
||
},
|
||
templatesSection: {
|
||
marginTop: 24,
|
||
},
|
||
templatesGrid: {
|
||
paddingHorizontal: 20,
|
||
flexDirection: 'row',
|
||
flexWrap: 'wrap',
|
||
gap: 12,
|
||
},
|
||
templateItem: {
|
||
flex: 1,
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
backgroundColor: '#FFFFFF',
|
||
minWidth: (screenWidth - 60) / 2, // 2列布局,考虑间距和padding
|
||
maxWidth: (screenWidth - 60) / 2,
|
||
aspectRatio: 2.2,
|
||
borderRadius: 16,
|
||
padding: 16,
|
||
justifyContent: 'center',
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 1 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 3,
|
||
elevation: 2,
|
||
},
|
||
templateIcon: {
|
||
width: 24,
|
||
height: 24,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
},
|
||
templateTitle: {
|
||
fontSize: 14,
|
||
color: '#374151',
|
||
fontWeight: '600',
|
||
lineHeight: 20,
|
||
marginLeft: 12,
|
||
},
|
||
}); |