feat: 更新 CLAUDE.md 文件及多个组件以优化用户体验和功能
- 更新 CLAUDE.md 文件,重构架构部分,增加认证和数据层的描述 - 在 GoalsScreen 中新增目标模板选择功能,支持用户选择和创建目标 - 在 CreateGoalModal 中添加初始数据支持,优化目标创建体验 - 新增 GoalTemplateModal 组件,提供目标模板选择界面 - 更新 NotificationHelpers,支持构建深度链接以便于导航 - 在 CoachScreen 中处理路由参数,增强用户交互体验 - 更新多个组件的样式和逻辑,提升整体用户体验 - 删除不再使用的中文回复规则文档
This commit is contained in:
@@ -44,9 +44,9 @@ export default function TabLayout() {
|
||||
case 'explore':
|
||||
return { icon: 'magnifyingglass.circle.fill', title: '发现' } as const;
|
||||
case 'coach':
|
||||
return { icon: 'person.3.fill', title: 'Seal' } as const;
|
||||
return { icon: 'message.fill', title: 'AI' } as const;
|
||||
case 'goals':
|
||||
return { icon: 'flag.fill', title: '目标' } as const;
|
||||
return { icon: 'flag.fill', title: '习惯' } as const;
|
||||
case 'statistics':
|
||||
return { icon: 'chart.pie.fill', title: '统计' } as const;
|
||||
case 'personal':
|
||||
|
||||
@@ -118,8 +118,17 @@ const CardType = {
|
||||
type CardType = typeof CardType[keyof typeof CardType];
|
||||
|
||||
|
||||
// 定义路由参数类型
|
||||
type CoachScreenParams = {
|
||||
name?: string;
|
||||
action?: 'diet' | 'weight' | 'mood' | 'workout';
|
||||
subAction?: 'record' | 'photo' | 'text' | 'card';
|
||||
meal?: 'breakfast' | 'lunch' | 'dinner' | 'snack';
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export default function CoachScreen() {
|
||||
const params = useLocalSearchParams<{ name?: string }>();
|
||||
const params = useLocalSearchParams<CoachScreenParams>();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const { isLoggedIn, pushIfAuthedElseLogin } = useAuthGuard();
|
||||
@@ -389,6 +398,69 @@ export default function CoachScreen() {
|
||||
};
|
||||
}, [insets.bottom]);
|
||||
|
||||
// 处理路由参数动作
|
||||
useEffect(() => {
|
||||
// 确保用户已登录且消息已加载
|
||||
if (!isLoggedIn || messages.length === 0) return;
|
||||
|
||||
// 检查是否有动作参数
|
||||
if (params.action) {
|
||||
const executeAction = async () => {
|
||||
try {
|
||||
switch (params.action) {
|
||||
case 'diet':
|
||||
if (params.subAction === 'card') {
|
||||
// 插入饮食记录卡片
|
||||
insertDietInputCard();
|
||||
} else if (params.subAction === 'record' && params.message) {
|
||||
// 直接发送预设的饮食记录消息
|
||||
const mealPrefix = params.meal ? `${getMealDisplayName(params.meal)}` : '';
|
||||
const message = `#记饮食:${mealPrefix}${decodeURIComponent(params.message)}`;
|
||||
await sendStream(message);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'weight':
|
||||
if (params.subAction === 'card') {
|
||||
// 插入体重记录卡片
|
||||
insertWeightInputCard();
|
||||
} else if (params.subAction === 'record' && params.message) {
|
||||
// 直接发送预设的体重记录消息
|
||||
const message = `#记体重:${decodeURIComponent(params.message)}`;
|
||||
await sendStream(message);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mood':
|
||||
// 跳转到心情记录页面
|
||||
pushIfAuthedElseLogin('/mood/calendar');
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn('未知的动作类型:', params.action);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('执行路由动作失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 延迟执行,确保页面已完全加载
|
||||
const timer = setTimeout(executeAction, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [params.action, params.subAction, params.meal, params.message, isLoggedIn, messages.length]);
|
||||
|
||||
// 获取餐次显示名称
|
||||
const getMealDisplayName = (meal: string): string => {
|
||||
const mealNames: Record<string, string> = {
|
||||
breakfast: '早餐',
|
||||
lunch: '午餐',
|
||||
dinner: '晚餐',
|
||||
snack: '加餐'
|
||||
};
|
||||
return mealNames[meal] || '';
|
||||
};
|
||||
|
||||
const streamAbortRef = useRef<{ abort: () => void } | null>(null);
|
||||
|
||||
// 组件卸载时清理流式请求和定时器
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CreateGoalModal from '@/components/CreateGoalModal';
|
||||
import { CreateGoalModal } from '@/components/CreateGoalModal';
|
||||
import GoalTemplateModal from '@/components/GoalTemplateModal';
|
||||
import { GoalsPageGuide } from '@/components/GoalsPageGuide';
|
||||
import { GuideTestButton } from '@/components/GuideTestButton';
|
||||
import { TaskCard } from '@/components/TaskCard';
|
||||
@@ -7,6 +8,7 @@ import { TaskProgressCard } from '@/components/TaskProgressCard';
|
||||
import { useGlobalDialog } from '@/components/ui/DialogProvider';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar';
|
||||
import { GoalTemplate } from '@/constants/goalTemplates';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
@@ -37,9 +39,7 @@ export default function GoalsScreen() {
|
||||
tasksLoading,
|
||||
tasksError,
|
||||
tasksPagination,
|
||||
completeLoading,
|
||||
completeError,
|
||||
skipLoading,
|
||||
skipError,
|
||||
} = useAppSelector((state) => state.tasks);
|
||||
|
||||
@@ -50,11 +50,13 @@ export default function GoalsScreen() {
|
||||
|
||||
const userProfile = useAppSelector((state) => state.user.profile);
|
||||
|
||||
const [showTemplateModal, setShowTemplateModal] = useState(false);
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [selectedFilter, setSelectedFilter] = useState<TaskFilterType>('all');
|
||||
const [modalKey, setModalKey] = useState(0); // 用于强制重新渲染弹窗
|
||||
const [showGuide, setShowGuide] = useState(false); // 控制引导显示
|
||||
const [selectedTemplateData, setSelectedTemplateData] = useState<Partial<CreateGoalRequest> | undefined>();
|
||||
|
||||
// 页面聚焦时重新加载数据
|
||||
useFocusEffect(
|
||||
@@ -141,6 +143,29 @@ export default function GoalsScreen() {
|
||||
const handleModalSuccess = () => {
|
||||
// 不需要在这里改变 modalKey,因为弹窗已经关闭了
|
||||
// 下次打开时会自动使用新的 modalKey
|
||||
setSelectedTemplateData(undefined);
|
||||
};
|
||||
|
||||
// 处理模板选择
|
||||
const handleSelectTemplate = (template: GoalTemplate) => {
|
||||
setSelectedTemplateData(template.data);
|
||||
setShowTemplateModal(false);
|
||||
setModalKey(prev => prev + 1);
|
||||
setShowCreateModal(true);
|
||||
};
|
||||
|
||||
// 处理创建自定义目标
|
||||
const handleCreateCustomGoal = () => {
|
||||
setSelectedTemplateData(undefined);
|
||||
setShowTemplateModal(false);
|
||||
setModalKey(prev => prev + 1);
|
||||
setShowCreateModal(true);
|
||||
};
|
||||
|
||||
// 打开模板选择弹窗
|
||||
const handleOpenTemplateModal = () => {
|
||||
setSelectedTemplateData(undefined);
|
||||
setShowTemplateModal(true);
|
||||
};
|
||||
|
||||
// 创建目标处理函数
|
||||
@@ -353,10 +378,10 @@ export default function GoalsScreen() {
|
||||
<View style={styles.header}>
|
||||
<View>
|
||||
<Text style={[styles.pageTitle, { color: '#FFFFFF' }]}>
|
||||
今日目标
|
||||
习惯养成
|
||||
</Text>
|
||||
<Text style={[styles.pageTitle2, { color: '#FFFFFF' }]}>
|
||||
让我们检查你的目标!
|
||||
自律让我更健康
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -378,10 +403,7 @@ export default function GoalsScreen() {
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.cardAddButton, { backgroundColor: colorTokens.primary }]}
|
||||
onPress={() => {
|
||||
setModalKey(prev => prev + 1); // 每次打开弹窗时使用新的 key
|
||||
setShowCreateModal(true);
|
||||
}}
|
||||
onPress={handleOpenTemplateModal}
|
||||
>
|
||||
<Text style={styles.cardAddButtonText}>+</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -420,14 +442,26 @@ export default function GoalsScreen() {
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 目标模板选择弹窗 */}
|
||||
<GoalTemplateModal
|
||||
visible={showTemplateModal}
|
||||
onClose={() => setShowTemplateModal(false)}
|
||||
onSelectTemplate={handleSelectTemplate}
|
||||
onCreateCustom={handleCreateCustomGoal}
|
||||
/>
|
||||
|
||||
{/* 创建目标弹窗 */}
|
||||
<CreateGoalModal
|
||||
key={modalKey}
|
||||
visible={showCreateModal}
|
||||
onClose={() => setShowCreateModal(false)}
|
||||
onClose={() => {
|
||||
setShowCreateModal(false);
|
||||
setSelectedTemplateData(undefined);
|
||||
}}
|
||||
onSubmit={handleCreateGoal}
|
||||
onSuccess={handleModalSuccess}
|
||||
loading={createLoading}
|
||||
initialData={selectedTemplateData}
|
||||
/>
|
||||
|
||||
{/* 目标页面引导 */}
|
||||
|
||||
Reference in New Issue
Block a user