import { useState, useEffect } from 'react'; import { Plus, Wind, Sparkles, Pencil, Trash2 } from 'lucide-react'; import { GlassCard, GlassButton, GlassInput, GlassTextarea } from '../components/ui'; import Modal from '../components/Modal'; import useStore from '../store/useStore'; import { analyzeLifeEvent } from '../services/ai'; import useTypewriterStream from '../hooks/useTypewriterStream'; /** * 格式化 AI 反馈内容的组件 */ const FeedbackContent = ({ content }) => { if (!content) return null; // 检查是否为结构化格式 (包含分隔符 --- 和标题标识 ####) const isStructured = content.includes('---') && content.includes('####'); if (!isStructured) { return (

{content}

); } // 解析结构化内容 const sections = content.split('---') .map(s => s.trim()) .filter(s => s && s.length > 0); return (
{sections.map((section, index) => { // 移除 #### 前缀 const cleanSection = section.replace(/^####\s*/, ''); // 提取标题 (通常在 【】 中) const titleMatch = cleanSection.match(/【(.*?)】/); const title = titleMatch ? titleMatch[1] : ''; // 提取正文 let body = cleanSection; if (titleMatch) { body = cleanSection.replace(titleMatch[0], '').trim(); } if (!title && !body) return null; return (
{title && (
{title}
)}
{body.split('\n').map((line, i) => { const trimmedLine = line.trim(); if (!trimmedLine) return null; // 简单的列表项处理 if (trimmedLine.startsWith('*') || trimmedLine.startsWith('-')) { return (
{trimmedLine.substring(1).trim()}
); } return

{trimmedLine}

; })}
); })}
); }; /** * TimelineView 组件 * 人生轨迹视图,显示和管理生命事件 */ const TimelineView = () => { const { lifeEvents, addLifeEvent, updateLifeEvent, deleteLifeEvent, loadLifeEvents } = useStore(); // 加载生命事件 useEffect(() => { loadLifeEvents().catch(() => { // 后端不可用时忽略错误 }); }, [loadLifeEvents]); // 模态框状态 const [isModalOpen, setIsModalOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [streamFeedback, setStreamFeedback] = useState(''); const feedbackWriter = useTypewriterStream({ interval: 30, step: 1 }); // 编辑模式状态:null 表示新增模式,有值表示编辑模式(存储事件 ID) const [editingEventId, setEditingEventId] = useState(null); // 删除确认状态 const [deleteConfirmId, setDeleteConfirmId] = useState(null); // 表单状态 const [eventForm, setEventForm] = useState({ title: '', time: '', content: '' }); /** * 打开新增模态框 */ const openAddModal = () => { setEditingEventId(null); setEventForm({ title: '', time: '', content: '' }); setStreamFeedback(''); feedbackWriter.reset(); setIsModalOpen(true); }; /** * 打开编辑模态框 * @param {Object} event - 要编辑的事件 */ const openEditModal = (event) => { setEditingEventId(event.id); setEventForm({ title: event.title || '', time: event.time || '', content: event.content || '' }); setStreamFeedback(event.aiFeedback || ''); feedbackWriter.reset(); setIsModalOpen(true); }; /** * 关闭模态框 */ const closeModal = () => { setIsModalOpen(false); setEditingEventId(null); setEventForm({ title: '', time: '', content: '' }); setStreamFeedback(''); feedbackWriter.reset(); }; /** * 处理表单提交(新增或编辑) */ const handleSubmit = async () => { if (!eventForm.title || !eventForm.time || !eventForm.content) { alert('请完整填写记录。'); return; } setIsLoading(true); feedbackWriter.reset(); try { setStreamFeedback(''); const aiFeedback = await analyzeLifeEvent(eventForm, { onDelta: (_delta, output) => { setStreamFeedback(output); feedbackWriter.push(output); } }); feedbackWriter.finish(aiFeedback); await feedbackWriter.waitForDone(); if (editingEventId) { // 编辑模式:调用更新接口 await updateLifeEvent({ id: editingEventId, ...eventForm, aiFeedback }); } else { // 新增模式:调用添加接口 await addLifeEvent({ ...eventForm, aiFeedback }); } // 重置表单并关闭模态框 closeModal(); } catch (error) { feedbackWriter.fail('AI 疗愈反馈生成失败,请稍后重试'); console.error('Failed to save event:', error); } finally { setIsLoading(false); } }; /** * 处理删除确认 * @param {string} id - 事件ID */ const handleDeleteConfirm = async (id) => { try { await deleteLifeEvent(id); setDeleteConfirmId(null); } catch (error) { console.error('Failed to delete event:', error); alert('删除失败,请稍后重试'); setDeleteConfirmId(null); } }; /** * 按事件时间倒序排列(最新的在最上面) * 空日期的事件排在最后 */ const sortedEvents = [...lifeEvents].sort((a, b) => { // 如果两个都没有时间,保持原顺序 if (!a.time && !b.time) return 0; // 没有时间的排在后面 if (!a.time) return 1; if (!b.time) return -1; // 按时间倒序(最新的在前) return new Date(b.time) - new Date(a.time); }); return (
{/* 标题区域 */}

人生轨迹

塑造你的每一刻,都被星辰见证。

记录足迹
{/* 时间线容器 */}
{sortedEvents.length > 0 &&
}
{sortedEvents.length > 0 ? ( sortedEvents.map((event) => (
{/* 时间线点 */}
{/* 事件卡片 */} {/* 操作按钮 */}

{event.title}

{event.tags && event.tags.length > 0 && ( {event.tags[0] === 'childhood' ? '童年' : event.tags[0] === 'joy' ? '高光' : event.tags[0] === 'low' ? '低谷' : event.tags[0]} )}
{event.time}

{event.content}

{/* AI 反馈区域 - 仅在有反馈时显示 */} {event.aiFeedback && (
引路人洞察
)}
)) ) : ( /* 空状态 */

执笔写下,哪些经历让你成为了现在的自己

)}
{/* 添加/编辑事件模态框 */}
setEventForm(prev => ({ ...prev, title: v }))} /> setEventForm(prev => ({ ...prev, time: v }))} /> setEventForm(prev => ({ ...prev, content: v }))} rows={5} /> {(streamFeedback || isLoading) && (
{feedbackWriter.isWaiting ? '正在理解这段经历' : feedbackWriter.isDraining ? '正在收束反馈' : '实时疗愈反馈'}
{feedbackWriter.visibleText ? :

AI 正在梳理你的生命轨迹,请稍候...

}
)} {isLoading ? '正在共鸣生命轨迹...' : '开启 AI 疗愈'}
{/* 删除确认模态框 */} setDeleteConfirmId(null)} title="确认删除" maxWidth="sm">

确定要删除这段人生足迹吗?此操作不可恢复,相关的 AI 洞察也将被删除。

setDeleteConfirmId(null)} className="flex-1" > 取消 handleDeleteConfirm(deleteConfirmId)} className="flex-1 bg-red-500/10 text-red-400 border-red-400/20 hover:bg-red-500/20" > 确认删除
); }; export default TimelineView;