import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { MoodItem } from '../types'; import { SimpleMoodBubble, MoodData } from './SimpleMoodBubble'; import { PageHeader } from './PageHeader'; import { ChatDialog } from './ChatDialog'; import { apiService } from '../services/api'; interface MoodViewProps { items: MoodItem[]; onClose: () => void; characterImageUrl?: string; onSendMessage: (message: string) => Promise; } interface MoodDetailData extends MoodData { // 扩展字段用于详情显示 } interface DayMoods { date: string; displayDate: string; moods: MoodData[]; moodCount: number; } export const MoodView: React.FC = ({ items, onClose, characterImageUrl, onSendMessage }) => { const [isChatOpen, setIsChatOpen] = useState(false); const [selectedMood, setSelectedMood] = useState(null); const [moodsData, setMoodsData] = useState([]); const [selectedDayIndex, setSelectedDayIndex] = useState(0); const [expandedDay, setExpandedDay] = useState(null); const scrollContainerRef = useRef(null); // 从后端加载完整的 moods 数据 useEffect(() => { const loadMoodsData = async () => { try { console.log('🫧 开始加载心情数据...'); const response = await apiService.getMoods(); console.log('📊 后端返回的心情数据:', response); // 显示最近30天的心情 const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000; const recentMoods = response.moods .filter((mood: any) => { const timestamp = new Date(mood.timestamp).getTime(); return timestamp >= thirtyDaysAgo; }) .map((mood: any) => ({ id: mood.record_id, type: mood.type, intensity: mood.intensity, timestamp: mood.timestamp, keywords: mood.keywords || [], originalText: mood.original_text || '', })); console.log('✨ 最终显示的心情数据:', recentMoods); setMoodsData(recentMoods); } catch (error) { console.error('❌ 加载心情数据失败:', error); const fallbackMoods = items.map(item => ({ id: item.id, type: item.type, intensity: item.intensity * 10, timestamp: new Date(item.date).toISOString(), keywords: [], originalText: '', })); setMoodsData(fallbackMoods); } }; loadMoodsData(); }, [items]); // 按日期分组心情数据 const groupedByDay = useMemo(() => { const groups = new Map(); moodsData.forEach(mood => { const date = new Date(mood.timestamp); const dateKey = date.toISOString().split('T')[0]; // YYYY-MM-DD if (!groups.has(dateKey)) { groups.set(dateKey, []); } groups.get(dateKey)!.push(mood); }); // 转换为数组并排序(最新的在前) const result: DayMoods[] = Array.from(groups.entries()) .map(([dateKey, moods]) => { const date = new Date(dateKey); const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); let displayDate = ''; if (dateKey === today.toISOString().split('T')[0]) { displayDate = '今天'; } else if (dateKey === yesterday.toISOString().split('T')[0]) { displayDate = '昨天'; } else { displayDate = date.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'short' }); } return { date: dateKey, displayDate, moods: moods.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() ), moodCount: moods.length, }; }) .sort((a, b) => b.date.localeCompare(a.date)); return result; }, [moodsData]); const handleMoodClick = useCallback((mood: MoodData | null) => { if (mood === null) { setSelectedMood(null); } else { setSelectedMood(mood as MoodDetailData); } }, []); const handleDayCardClick = (dayData: DayMoods) => { if (expandedDay === dayData.date) { setExpandedDay(null); } else { setExpandedDay(dayData.date); } }; const scrollToIndex = (index: number) => { if (scrollContainerRef.current) { const cardWidth = 320; // 卡片宽度 + gap scrollContainerRef.current.scrollTo({ left: index * cardWidth, behavior: 'smooth' }); } setSelectedDayIndex(index); }; const formatDate = (timestamp: string) => { const date = new Date(timestamp); return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); }; return (
{/* Background Overlay */}
{/* Content Layer */}
{/* 页面头部 */} setIsChatOpen(true)} characterImageUrl={characterImageUrl} /> {/* 塔罗牌式卡片轮播 */}
{/* 空状态 */} {groupedByDay.length === 0 && (

暂无心情记录

)} {/* 卡片容器 */} {groupedByDay.length > 0 && (
{/* 卡片堆叠效果 */}
{groupedByDay.map((dayData, index) => { const offset = index - selectedDayIndex; const isActive = index === selectedDayIndex; const isPrev = offset === -1; const isNext = offset === 1; const isVisible = Math.abs(offset) <= 1; return (
); })}
{/* 左右导航按钮 */}
)} {/* 指示器 */} {groupedByDay.length > 0 && (
{groupedByDay.map((_, index) => (
)} {/* 使用说明 */}

左右滑动或点击箭头切换日期

{/* 展开的某一天详情 - 全屏气泡池 */} {expandedDay && (
{/* 返回按钮 */}

{groupedByDay.find(d => d.date === expandedDay)?.displayDate}

{/* 占位 */}
{/* 气泡池容器 */}
d.date === expandedDay)?.moods || []} onMoodClick={handleMoodClick} />
{/* 提示文字 */}

左键查看详情 · 右键取消 · 拖动互动

)} {/* 心情详情弹窗 */} {selectedMood && (
setSelectedMood(null)}>
e.stopPropagation()} > {/* 关闭按钮 */} {/* 内容 */}
{/* 心情类型 */}

{selectedMood.type}

{formatDate(selectedMood.timestamp)}

{/* 强度指示器 */}
情绪强度 {selectedMood.intensity}/10
{/* 原文内容 */} {selectedMood.originalText && (

记录原文

{selectedMood.originalText}

)} {/* 关键词 */} {selectedMood.keywords && selectedMood.keywords.length > 0 && (

关键词

{selectedMood.keywords.map((keyword, index) => ( {keyword} ))}
)} {/* 提示 */}

"每一种情绪都值得被记录和珍惜"

)} {/* 对话弹窗 */} setIsChatOpen(false)} characterImageUrl={characterImageUrl} onSendMessage={onSendMessage} />
); }; // 辅助函数:获取心情颜色 function getMoodColor(type: string): string { const colors: Record = { '喜悦': '#FED7AA', '开心': '#FECACA', '兴奋': '#FEF08A', '快乐': '#FDE68A', '愉悦': '#FCA5A5', '欣喜': '#FDBA74', '惊喜': '#FCD34D', '满足': '#FBB6CE', '成就': '#F9A8D4', '希望': '#FDE047', '平静': '#BFDBFE', '放松': '#D9F99D', '宁静': '#A5F3FC', '清新': '#99F6E4', '温柔': '#E9D5FF', '温暖': '#FBCFE8', '充实': '#C7D2FE', '积极': '#BAE6FD', '憧憬': '#DDD6FE', '焦虑': '#DDD6FE', '紧张': '#E9D5FF', '悲伤': '#CBD5E1', '疲惫': '#E0E7FF', '困倦': '#F3E8FF', '沮丧': '#D1D5DB', '孤独': '#E5E7EB', '烦躁': '#FEE2E2', '感动': '#F9A8D4', '思念': '#C4B5FD', '感慨': '#D8B4FE', }; return colors[type] || '#E2E8F0'; }