Files
happy-life-star/prototype/app.js
T
peanut 755059807a feat: 优化管理后台页面UI、修复TS编译错误、新增人生事件模块
- 优化 AI 配置列表页面:重构统计卡片、搜索表单、表格列展示
- 修复 3 处 TypeScript TS6133 编译错误,恢复构建
- 新增管理员修改密码和重置密码功能
- 优化小程序多个页面样式和交互
- 人生事件模块完善

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 23:23:09 +08:00

259 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { state } from './data.js';
import { RegistrationWizard, AppPages } from './components.js';
const DOM = {
onboarding: document.getElementById('onboarding'),
wizardContent: document.getElementById('wizard-content'),
mainContent: document.getElementById('main-content'),
pageRender: document.getElementById('page-render'),
bottomNav: document.getElementById('bottom-nav'),
musicToggle: document.getElementById('music-toggle'),
bgMusic: document.getElementById('bg-music'),
musicDisc: document.getElementById('music-disc'),
profileBtn: document.getElementById('profile-btn')
};
function initStars() {
const starContainer = document.getElementById('stars');
starContainer.innerHTML = '';
for (let i = 0; i < 60; i++) {
const star = document.createElement('div');
star.className = 'star';
const size = Math.random() * 3 + 1;
const xMove = (Math.random() - 0.5) * 100;
const yMove = (Math.random() - 0.5) * 100;
const duration = 15 + Math.random() * 20;
const delay = Math.random() * -20;
const opacity = 0.2 + Math.random() * 0.5;
star.style.width = `${size}px`;
star.style.height = `${size}px`;
star.style.left = `${Math.random() * 100}%`;
star.style.top = `${Math.random() * 100}%`;
star.style.setProperty('--x', `${xMove}px`);
star.style.setProperty('--y', `${yMove}px`);
star.style.setProperty('--duration', `${duration}s`);
star.style.setProperty('--opacity', opacity);
star.style.animationDelay = `${delay}s`;
starContainer.appendChild(star);
}
}
function updateWizard() {
DOM.wizardContent.innerHTML = `
<div class="flex-1 overflow-y-auto custom-scroll">
${RegistrationWizard.renderStep(state.onboardingStep, state.user)}
</div>
<div class="pt-6 flex justify-between items-center">
<div class="flex gap-1.5">
${[0, 1, 2, 3, 4].map(i => `<div class="w-6 h-1 rounded-full transition-all duration-700 ${i === state.onboardingStep ? 'bg-purple-400 w-10 shadow-[0_0_12px_rgba(168,85,247,0.6)]' : 'bg-white/10'}"></div>`).join('')}
</div>
<button id="next-step" class="px-10 py-3.5 bg-gradient-to-r from-purple-500 to-violet-400 rounded-2xl text-white text-sm font-bold shadow-xl shadow-purple-500/10 active:scale-95 transition-all">
${state.onboardingStep === 4 ? '同步星海' : '继续'}
</button>
</div>
`;
lucide.createIcons();
document.getElementById('next-step').addEventListener('click', () => {
saveCurrentStepData();
if (state.onboardingStep < 4) {
state.onboardingStep++;
gsap.fromTo('#wizard-content > div:first-child', { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out" });
updateWizard();
} else {
completeOnboarding();
}
});
}
function saveCurrentStepData() {
const s = state.onboardingStep;
if (s === 0) {
state.user.nickname = document.getElementById('nickname').value;
state.user.gender = document.getElementById('gender').value;
state.user.zodiac = document.getElementById('zodiac').value;
state.user.mbti = document.getElementById('mbti').value;
state.user.hobbies = document.getElementById('hobbies').value.split(',').map(v => v.trim()).filter(v => v);
} else if (s === 1) {
state.user.childhood = document.getElementById('childhood').value;
state.user.childhoodDate = document.getElementById('childhood-date').value;
} else if (s === 2) {
state.user.happyMoment = document.getElementById('happy-moment').value;
state.user.happyDate = document.getElementById('happy-date').value;
} else if (s === 3) {
state.user.lowPoint = document.getElementById('low-point').value;
state.user.lowDate = document.getElementById('low-date').value;
} else if (s === 4) {
state.user.aspirations = document.getElementById('aspirations').value;
}
}
function completeOnboarding() {
gsap.to(DOM.onboarding, {
opacity: 0,
duration: 1.5,
ease: "power3.inOut",
onComplete: () => {
DOM.onboarding.style.display = 'none';
DOM.mainContent.style.display = 'flex';
DOM.bottomNav.style.display = 'flex';
switchTab('record');
}
});
}
function switchTab(tab) {
document.querySelectorAll('.nav-item').forEach(el => {
el.classList.toggle('active', el.dataset.tab === tab);
});
let content = "";
if (tab === 'record') content = AppPages.record(state.events);
if (tab === 'script') content = AppPages.script(state.scripts, state.isGenerating, state.scriptConfig, state.npcConfig, state.user, state.customPersonas);
if (tab === 'path') content = AppPages.path(state.currentPath);
if (tab === 'profile') content = AppPages.profile(state.user);
DOM.pageRender.innerHTML = content;
lucide.createIcons();
if (tab === 'record') {
document.getElementById('save-event').addEventListener('click', handleSaveEvent);
}
if (tab === 'script') {
document.getElementById('gen-script')?.addEventListener('click', handleGenerateScript);
document.getElementById('edit-name')?.addEventListener('input', (e) => state.user.nickname = e.target.value);
document.getElementById('edit-zodiac')?.addEventListener('change', (e) => state.user.zodiac = e.target.value);
document.getElementById('edit-mbti')?.addEventListener('change', (e) => state.user.mbti = e.target.value);
document.getElementById('edit-job')?.addEventListener('input', (e) => state.user.profession = e.target.value);
document.getElementById('add-persona-btn')?.addEventListener('click', () => {
const name = document.getElementById('npc-name').value;
const role = document.getElementById('npc-role').value;
if (name) {
state.customPersonas.push({ name, role, id: Date.now() });
switchTab('script');
}
});
document.querySelectorAll('.delete-persona-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const idx = e.currentTarget.dataset.idx;
state.customPersonas.splice(idx, 1);
switchTab('script');
});
});
document.getElementById('npc-name')?.addEventListener('input', (e) => state.npcConfig.name = e.target.value);
document.getElementById('npc-role')?.addEventListener('change', (e) => state.npcConfig.role = e.target.value);
document.getElementById('npc-relation')?.addEventListener('change', (e) => state.npcConfig.relation = e.target.value);
document.getElementById('npc-desc')?.addEventListener('input', (e) => state.npcConfig.desc = e.target.value);
document.querySelectorAll('.style-btn').forEach(btn => {
btn.addEventListener('click', () => {
state.scriptConfig.style = btn.dataset.val;
switchTab('script');
});
});
document.querySelectorAll('.length-btn').forEach(btn => {
btn.addEventListener('click', () => {
state.scriptConfig.length = btn.dataset.val;
switchTab('script');
});
});
document.querySelectorAll('.select-script').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = e.currentTarget.dataset.id;
state.currentPath = state.scripts.find(s => s.id == id);
switchTab('path');
});
});
}
}
function handleSaveEvent() {
const title = document.getElementById('event-title').value;
const time = document.getElementById('event-time').value;
const content = document.getElementById('event-content').value;
if (!title || !content) return;
const newEvent = {
id: Date.now(),
title,
time: time || new Date().toISOString().split('T')[0],
content,
aiReply: "星河守望者正在解读这段紫色波频...",
isNew: true
};
state.events.unshift(newEvent);
switchTab('record');
setTimeout(() => {
const fullReply = `这段记忆碎片在星海中漾起涟漪。你在${title}中展现的特质,正在重新定义你人生OS的底层代码。继续保持这份觉知。`;
state.events[0].aiReply = fullReply;
state.events[0].isNew = false;
switchTab('record');
}, 1500);
}
function handleGenerateScript() {
const theme = document.getElementById('script-theme').value;
if (!theme) return;
if (state.isGenerating) return;
state.isGenerating = true;
switchTab('script');
setTimeout(() => {
const { name, role, relation, desc } = state.npcConfig;
const npcFrag = name ? `,在 ${name}(你的${role},关系:${relation})的陪伴下,` : "";
const script = {
id: Date.now(),
title: `${theme}》· 星源篇`,
summary: `基于你(${state.user.nickname},一名${state.user.profession})过往的紫气东来,在${state.scriptConfig.style}的世界观中${npcFrag}你将重新定义自我。${desc ? '命运齿轮已然转动:' + desc : ''}`,
persona: state.user.mbti || "追光者",
steps: [
{ task: "紫微入命", desc: `在"${theme}"的意志中开启新的人生循环。`, done: true },
{ task: "能量共振", desc: name ? `${name} 达成灵魂契约。` : "在孤独中寻找真理,掌握世界的运行法则。", done: true },
{ task: "代码重写", desc: "在最为艰难的时刻,将过去的所有记录化作突围的力量。", done: false },
{ task: "最终归宿", desc: "达成人生OS的最高成就,实现与理想自我的合一。", done: false }
]
};
state.scripts.unshift(script);
state.isGenerating = false;
switchTab('script');
}, 2500);
}
DOM.musicToggle.addEventListener('click', () => {
if (state.isPlaying) {
DOM.bgMusic.pause();
DOM.musicDisc.classList.remove('animate-spin-slow');
DOM.musicToggle.classList.add('opacity-40');
} else {
DOM.bgMusic.play();
DOM.musicDisc.classList.add('animate-spin-slow');
DOM.musicToggle.classList.remove('opacity-40');
}
state.isPlaying = !state.isPlaying;
});
DOM.bottomNav.addEventListener('click', (e) => {
const btn = e.target.closest('button');
if (btn) switchTab(btn.dataset.tab);
});
DOM.profileBtn.addEventListener('click', () => switchTab('profile'));
initStars();
updateWizard();
lucide.createIcons();