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;