前端重构实现
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Plus, Wind, Sparkles } 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';
|
||||
|
||||
/**
|
||||
* TimelineView 组件
|
||||
* 生命长河视图,显示和管理生命事件
|
||||
*/
|
||||
const TimelineView = () => {
|
||||
const { lifeEvents, addLifeEvent, loadLifeEvents } = useStore();
|
||||
|
||||
// 加载生命事件
|
||||
useEffect(() => {
|
||||
loadLifeEvents().catch(() => {
|
||||
// 后端不可用时忽略错误
|
||||
});
|
||||
}, [loadLifeEvents]);
|
||||
|
||||
// 模态框状态
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// 表单状态
|
||||
const [eventForm, setEventForm] = useState({
|
||||
title: '',
|
||||
time: '',
|
||||
content: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* 处理表单提交
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (!eventForm.title || !eventForm.time || !eventForm.content) {
|
||||
alert('请完整填写记录。');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
// 调用 AI 分析
|
||||
const aiFeedback = await analyzeLifeEvent(eventForm);
|
||||
|
||||
// 添加事件
|
||||
addLifeEvent({
|
||||
...eventForm,
|
||||
aiFeedback
|
||||
});
|
||||
|
||||
// 重置表单并关闭模态框
|
||||
setEventForm({ title: '', time: '', content: '' });
|
||||
setIsModalOpen(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to analyze event:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 按时间倒序排列事件
|
||||
*/
|
||||
const sortedEvents = [...lifeEvents].sort(
|
||||
(a, b) => new Date(b.time) - new Date(a.time)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* 标题区域 */}
|
||||
<div className="flex justify-between items-end mb-12">
|
||||
<div>
|
||||
<h3 className="text-4xl font-serif text-white/90">生命长河</h3>
|
||||
<p className="text-sm text-white/30 mt-2">塑造你的每一刻,都被星辰见证。</p>
|
||||
</div>
|
||||
<GlassButton
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="px-6 py-3 rounded-full text-sm font-bold flex items-center gap-2 bg-orange-200/5 text-orange-200 border-orange-200/20 shadow-lg"
|
||||
>
|
||||
<Plus className="w-4 h-4" /> 记录足迹
|
||||
</GlassButton>
|
||||
</div>
|
||||
|
||||
{/* 时间线容器 */}
|
||||
<div className="relative pl-8">
|
||||
{sortedEvents.length > 0 && <div className="timeline-line" />}
|
||||
|
||||
<div className="space-y-10">
|
||||
{sortedEvents.length > 0 ? (
|
||||
sortedEvents.map((event) => (
|
||||
<div key={event.id} className="relative group">
|
||||
{/* 时间线点 */}
|
||||
<div className="timeline-dot absolute left-[-39px] top-6 z-10" />
|
||||
|
||||
{/* 事件卡片 */}
|
||||
<GlassCard className="border-white/5 hover:border-orange-200/20 transition-all duration-700">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<h4 className="text-xl font-medium text-white/80">{event.title}</h4>
|
||||
<span className="text-[10px] font-mono tracking-widest text-white/30 uppercase">
|
||||
{event.time}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/60 leading-relaxed mb-6">
|
||||
{event.content}
|
||||
</p>
|
||||
|
||||
{/* AI 反馈区域 */}
|
||||
<div className="ai-glow-card p-5 rounded-2xl bg-orange-200/[0.02] border border-orange-200/5">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Sparkles className="w-3 h-3 text-orange-200" />
|
||||
<span className="text-[9px] uppercase tracking-[0.2em] text-orange-200/60 font-bold">
|
||||
引路人洞察
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs italic text-white/50 leading-loose">
|
||||
{event.aiFeedback}
|
||||
</p>
|
||||
</div>
|
||||
</GlassCard>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
/* 空状态 */
|
||||
<div className="flex flex-col items-center justify-center py-32 text-center opacity-30">
|
||||
<Wind className="w-12 h-12 mb-4" />
|
||||
<p className="font-serif italic text-lg">此间尚无回响,等待你执笔...</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 添加事件模态框 */}
|
||||
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title="记录足迹">
|
||||
<div className="space-y-6">
|
||||
<GlassInput
|
||||
label="事件标题"
|
||||
placeholder="给这段经历起个名字"
|
||||
value={eventForm.title}
|
||||
onChange={(v) => setEventForm(prev => ({ ...prev, title: v }))}
|
||||
/>
|
||||
<GlassInput
|
||||
label="发生时间"
|
||||
type="date"
|
||||
value={eventForm.time}
|
||||
onChange={(v) => setEventForm(prev => ({ ...prev, time: v }))}
|
||||
/>
|
||||
<GlassTextarea
|
||||
label="经历详情"
|
||||
placeholder="当时发生了什么?你的感受如何?"
|
||||
value={eventForm.content}
|
||||
onChange={(v) => setEventForm(prev => ({ ...prev, content: v }))}
|
||||
rows={5}
|
||||
/>
|
||||
<GlassButton
|
||||
variant="primary"
|
||||
onClick={handleSubmit}
|
||||
loading={isLoading}
|
||||
className="w-full"
|
||||
>
|
||||
{isLoading ? '正在共鸣生命轨迹...' : '开启 AI 疗愈'}
|
||||
</GlassButton>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimelineView;
|
||||
Reference in New Issue
Block a user