优化调整
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const LIFE_EVENTS_STORAGE_KEY = 'kaixinapp_life_events_v1';
|
||||
let lifeEvents = [];
|
||||
let nextLifeEventId = 1;
|
||||
|
||||
const addLifeEventBtn = document.getElementById('add-life-event-btn');
|
||||
const lifeEventsTimelineContainer = document.getElementById('life-events-timeline');
|
||||
const lifeEventsEmptyState = document.getElementById('life-events-empty');
|
||||
|
||||
const addEventModal = document.getElementById('add-event-modal');
|
||||
const addEventModalContent = document.getElementById('add-event-modal-content');
|
||||
const cancelAddEventBtn = document.getElementById('cancel-add-event');
|
||||
const lifeEventForm = document.getElementById('life-event-form');
|
||||
|
||||
const writeLetterModal = document.getElementById('write-letter-modal');
|
||||
const writeLetterModalContent = document.getElementById('write-letter-modal-content');
|
||||
const closeLetterModalBtn = document.getElementById('close-letter-modal');
|
||||
const letterEventTitle = document.getElementById('letter-event-title');
|
||||
const letterPlaceholder = document.getElementById('letter-placeholder');
|
||||
const letterFinalContent = document.getElementById('letter-final-content');
|
||||
const regenerateLetterBtn = document.getElementById('regenerate-letter-btn');
|
||||
const copyLetterBtn = document.getElementById('copy-letter-btn');
|
||||
let activeLetterEventId = null;
|
||||
|
||||
function loadLifeEvents() {
|
||||
const stored = localStorage.getItem(LIFE_EVENTS_STORAGE_KEY);
|
||||
if (stored) {
|
||||
lifeEvents = JSON.parse(stored);
|
||||
const maxId = lifeEvents.reduce((max, e) => Math.max(max, e.id), 0);
|
||||
nextLifeEventId = maxId + 1;
|
||||
} else {
|
||||
lifeEvents = [{
|
||||
id: 1,
|
||||
date: '2024-06-15',
|
||||
title: '大学毕业典礼',
|
||||
content: '四年的大学生活画上了句号。穿着学士服,和朋友、老师们告别,心中充满了不舍和对未来的憧憬。这是一个时代的结束,也是一个新开始。',
|
||||
type: 'positive',
|
||||
aiAnalysis: {
|
||||
title: '你做得很棒!',
|
||||
response: '我好喜欢你记录下这段记忆。<br>你在这件事情里,展现了【坚持】、【自我支持】和【成长】。<br>别小看这一刻的你,它证明了:你,是可以做到的。',
|
||||
keywords: ['坚持', '成长', '新起点'],
|
||||
emotionTags: ['自豪', '憧憬', '不舍']
|
||||
}
|
||||
}, {
|
||||
id: 2,
|
||||
date: '2023-03-20',
|
||||
title: '一次重要的面试失败',
|
||||
content: '为心仪的公司准备了很久,但最终还是失败了。感觉很失落,甚至开始怀疑自己的能力。花了几天时间才慢慢走出来。',
|
||||
type: 'negative',
|
||||
aiAnalysis: {
|
||||
title: '这段经历可能对你带来的影响…',
|
||||
response: '你提到当时很难过,也许是因为你在那时没有得到你真正渴望的回应。<br>从那以后,这段经历可能让你在类似场景里格外敏感——<br>这不是脆弱,而是你曾经努力保护自己留下的本能。<br><br>开开理解你,也想和你一起慢慢松开这段结。',
|
||||
keywords: ['挫折', '反思', '坚韧'],
|
||||
emotionTags: ['失落', '焦虑', '怀疑']
|
||||
}
|
||||
}];
|
||||
nextLifeEventId = 3;
|
||||
saveLifeEvents();
|
||||
}
|
||||
}
|
||||
|
||||
function saveLifeEvents() {
|
||||
localStorage.setItem(LIFE_EVENTS_STORAGE_KEY, JSON.stringify(lifeEvents));
|
||||
}
|
||||
|
||||
function renderLifeEvents() {
|
||||
if (!lifeEventsTimelineContainer) return;
|
||||
if (lifeEvents.length === 0) {
|
||||
lifeEventsEmptyState.classList.remove('hidden');
|
||||
lifeEventsTimelineContainer.classList.add('hidden');
|
||||
} else {
|
||||
lifeEventsEmptyState.classList.add('hidden');
|
||||
lifeEventsTimelineContainer.classList.remove('hidden');
|
||||
|
||||
lifeEvents.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
|
||||
lifeEventsTimelineContainer.innerHTML = lifeEvents.map((event, index) => {
|
||||
const isLastItem = index === lifeEvents.length - 1;
|
||||
const cardBg = event.type === 'positive' ? 'bg-emerald-500/5 border-emerald-500/20' : 'bg-red-500/5 border-red-500/20';
|
||||
const accentColor = event.type === 'positive' ? 'text-emerald-600' : 'text-red-600';
|
||||
const icon = event.type === 'positive' ? 'sparkles' : 'heart-crack';
|
||||
|
||||
return `
|
||||
<div class="relative pl-12 sm:pl-16 pb-4">
|
||||
<div class="absolute left-0 top-0 text-right w-10 sm:w-12">
|
||||
<p class="text-sm font-semibold text-text-dark">${new Date(event.date).getFullYear()}</p>
|
||||
<p class="text-xs text-text-medium">${new Date(event.date).toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' }).replace('/', '-')}</p>
|
||||
</div>
|
||||
<div class="absolute left-[48px] sm:left-[60px] top-2.5 w-4 h-4 bg-tech-blue rounded-full border-4 border-light-gray z-10"></div>
|
||||
${!isLastItem ? '<div class="absolute left-[55px] sm:left-[67px] top-4 h-full border-l-2 border-gray-200"></div>' : ''}
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-lg border border-gray-200/50 overflow-hidden">
|
||||
<div class="p-6">
|
||||
<h3 class="text-2xl font-bold text-text-dark mb-2">${event.title}</h3>
|
||||
<p class="text-text-medium leading-relaxed prose prose-sm max-w-none">${event.content}</p>
|
||||
</div>
|
||||
|
||||
<div class="${cardBg} p-6">
|
||||
<h4 class="font-bold mb-3 flex items-center gap-2 ${accentColor}">
|
||||
<i data-lucide="${icon}" class="w-5 h-5"></i>
|
||||
${event.aiAnalysis.title}
|
||||
</h4>
|
||||
<div class="text-sm leading-6 text-gray-700/80 mb-4">${event.aiAnalysis.response}</div>
|
||||
<div class="space-y-3 text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-gray-600 flex-shrink-0">成长关键词:</span>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
${event.aiAnalysis.keywords.map(k => `<span class="bg-white/60 text-gray-700 text-xs font-medium px-2 py-1 rounded-md">${k}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold text-gray-600 flex-shrink-0">情绪标签:</span>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
${event.aiAnalysis.emotionTags.map(t => `<span class="bg-white/60 text-gray-700 text-xs font-medium px-2 py-1 rounded-md">${t}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white/50 p-4 flex justify-end items-center space-x-3 border-t border-gray-200/50">
|
||||
<button class="text-sm font-semibold text-text-medium hover:text-tech-blue transition-colors flex items-center gap-2">
|
||||
<i data-lucide="search" class="w-4 h-4"></i>
|
||||
探索这段经历
|
||||
</button>
|
||||
<button data-letter-event-id="${event.id}" class="text-sm font-semibold text-white bg-warm-orange px-4 py-2 rounded-lg hover:bg-orange-600 transition flex items-center gap-2">
|
||||
<i data-lucide="mail" class="w-4 h-4"></i>
|
||||
写给当时的自己
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
attachLifeEventButtonListeners();
|
||||
}
|
||||
}
|
||||
|
||||
function attachLifeEventButtonListeners() {
|
||||
document.querySelectorAll('[data-letter-event-id]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const eventId = btn.dataset.letterEventId;
|
||||
openWriteLetterModal(eventId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function openModalAnimation(modal, content) {
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
setTimeout(() => {
|
||||
modal.classList.remove('opacity-0');
|
||||
content.classList.remove('scale-95', 'opacity-0');
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function closeModalAnimation(modal, content) {
|
||||
modal.classList.add('opacity-0');
|
||||
content.classList.add('scale-95', 'opacity-0');
|
||||
setTimeout(() => {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function showAddEventModal() {
|
||||
lifeEventForm.reset();
|
||||
document.getElementById('event-date').value = new Date().toISOString().split('T')[0];
|
||||
openModalAnimation(addEventModal, addEventModalContent);
|
||||
}
|
||||
|
||||
function hideAddEventModal() {
|
||||
closeModalAnimation(addEventModal, addEventModalContent);
|
||||
}
|
||||
|
||||
function handleLifeEventFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const event = {
|
||||
id: nextLifeEventId++,
|
||||
date: formData.get('date'),
|
||||
title: formData.get('title'),
|
||||
content: formData.get('content'),
|
||||
type: formData.get('eventType'),
|
||||
aiAnalysis: generateAiAnalysis(formData.get('content'), formData.get('eventType'))
|
||||
};
|
||||
lifeEvents.push(event);
|
||||
saveLifeEvents();
|
||||
renderLifeEvents();
|
||||
hideAddEventModal();
|
||||
}
|
||||
|
||||
function generateAiAnalysis(content, type) {
|
||||
const analysis = {};
|
||||
if (type === 'positive') {
|
||||
analysis.title = '你做得很棒!';
|
||||
analysis.response = '我好喜欢你记录下这段记忆。<br>你在这件事情里,展现了【坚持】、【自我支持】和【成长】。<br>别小看这一刻的你,它证明了:你,是可以做到的。';
|
||||
analysis.keywords = ['坚持', '自我支持', '成长'];
|
||||
analysis.emotionTags = ['快乐', '成就感', '自豪'];
|
||||
} else {
|
||||
analysis.title = '这段经历可能对你带来的影响…';
|
||||
analysis.response = '你提到当时很难过,也许是因为你在那时没有得到你真正渴望的回应。<br>从那以后,这段经历可能让你在类似场景里格外敏感——<br>这不是脆弱,而是你曾经努力保护自己留下的本能。<br><br>开开理解你,也想和你一起慢慢松开这段结。';
|
||||
analysis.keywords = ['反思', '坚韧', '接纳'];
|
||||
analysis.emotionTags = ['悲伤', '脆弱', '思考'];
|
||||
}
|
||||
if (content.includes('旅行')) analysis.keywords.push('探索');
|
||||
if (content.includes('工作') || content.includes('面试')) analysis.keywords.push('职业');
|
||||
if (content.includes('学习') || content.includes('毕业')) analysis.keywords.push('学业');
|
||||
return analysis;
|
||||
}
|
||||
|
||||
function openWriteLetterModal(eventId) {
|
||||
const event = lifeEvents.find(e => e.id == eventId);
|
||||
if (!event) return;
|
||||
|
||||
activeLetterEventId = eventId;
|
||||
letterEventTitle.textContent = event.title;
|
||||
letterPlaceholder.style.display = 'block';
|
||||
letterFinalContent.classList.add('hidden');
|
||||
letterFinalContent.innerHTML = '';
|
||||
|
||||
openModalAnimation(writeLetterModal, writeLetterModalContent);
|
||||
generateLetter(event);
|
||||
}
|
||||
|
||||
function hideWriteLetterModal() {
|
||||
closeModalAnimation(writeLetterModal, writeLetterModalContent);
|
||||
}
|
||||
|
||||
function generateLetter(event) {
|
||||
letterPlaceholder.style.display = 'block';
|
||||
letterFinalContent.classList.add('hidden');
|
||||
|
||||
setTimeout(() => {
|
||||
let letter;
|
||||
if (event.type === 'positive') {
|
||||
letter = `<p>亲爱的,在【${event.date}】的你:</p>
|
||||
<p>你好呀!我是来自未来的你,特地让开开捎来这封信。</p>
|
||||
<p>我知道,在【${event.title}】的那一刻,你的心里一定充满了阳光。你所感受到的那种【${event.aiAnalysis.emotionTags.join('、')}】的情绪,是那么真实和宝贵。请一定好好珍藏这份感觉。</p>
|
||||
<p>你当时展现出的【${event.aiAnalysis.keywords.join('、')}】的品质,在未来的日子里,也一直闪闪发光,帮助我走了很远的路。谢谢你,当时的你,那么勇敢,那么棒。</p>
|
||||
<p>请继续带着这份光芒走下去吧!未来可期!</p>
|
||||
<br><p class="text-right">爱你的,<br>未来的自己</p>`;
|
||||
} else {
|
||||
letter = `<p>亲爱的,在【${event.date}】的你:</p>
|
||||
<p>你好。当你读到这封信时,我知道你正在经历【${event.title}】的艰难时刻,心里可能充满了【${event.aiAnalysis.emotionTags.join('、')}】的复杂感受。</p>
|
||||
<p>我想告诉你,没关系,一切都会过去的。你当时的感受是完全正常的,请允许自己悲伤和脆弱。这不是你的错。这段经历虽然痛苦,但它也让你学会了【${event.aiAnalysis.keywords.join('、')}】。你比自己想象的要坚强得多。</p>
|
||||
<p>请相信,未来的你,也就是我,已经从这段经历中走了出来,并且变得更加完整和强大。所以,请抱抱自己,告诉自己你已经做得很好了。</p>
|
||||
<br><p class="text-right">永远支持你的,<br>未来的自己</p>`;
|
||||
}
|
||||
|
||||
letterFinalContent.innerHTML = letter;
|
||||
letterPlaceholder.style.display = 'none';
|
||||
letterFinalContent.classList.remove('hidden');
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function handleCopyLetter() {
|
||||
const content = letterFinalContent.innerText;
|
||||
navigator.clipboard.writeText(content).then(() => {
|
||||
const copyButton = document.getElementById('copy-letter-btn');
|
||||
const originalText = copyButton.innerHTML;
|
||||
copyButton.innerHTML = `<i data-lucide="check" class="w-4 h-4"></i> <span>已复制!</span>`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
setTimeout(() => {
|
||||
copyButton.innerHTML = originalText;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
alert('复制失败');
|
||||
});
|
||||
}
|
||||
|
||||
if(addLifeEventBtn) {
|
||||
addLifeEventBtn.addEventListener('click', showAddEventModal);
|
||||
cancelAddEventBtn.addEventListener('click', hideAddEventModal);
|
||||
addEventModal.addEventListener('click', (e) => {
|
||||
if (e.target === addEventModal) hideAddEventModal();
|
||||
});
|
||||
lifeEventForm.addEventListener('submit', handleLifeEventFormSubmit);
|
||||
|
||||
closeLetterModalBtn.addEventListener('click', hideWriteLetterModal);
|
||||
writeLetterModal.addEventListener('click', (e) => {
|
||||
if (e.target === writeLetterModal) hideWriteLetterModal();
|
||||
});
|
||||
regenerateLetterBtn.addEventListener('click', () => {
|
||||
const event = lifeEvents.find(e => e.id == activeLetterEventId);
|
||||
if(event) generateLetter(event);
|
||||
});
|
||||
copyLetterBtn.addEventListener('click', handleCopyLetter);
|
||||
|
||||
loadLifeEvents();
|
||||
renderLifeEvents();
|
||||
}
|
||||
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user