添加用户资料点击逻辑,更新页面UI

This commit is contained in:
Conner.G
2025-12-21 20:31:42 +08:00
parent 6b9b74c9e4
commit fa57938a9d
12 changed files with 451 additions and 89 deletions
+11
View File
@@ -64,6 +64,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -1399,6 +1400,7 @@
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1440,6 +1442,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1599,6 +1602,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -1912,6 +1916,7 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -3052,6 +3057,7 @@
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -3078,6 +3084,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -3125,6 +3132,7 @@
"resolved": "https://registry.npmmirror.com/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3134,6 +3142,7 @@
"resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -3399,6 +3408,7 @@
"resolved": "https://registry.npmmirror.com/rolldown-vite/-/rolldown-vite-7.2.5.tgz",
"integrity": "sha512-u09tdk/huMiN8xwoiBbig197jKdCamQTtOruSalOzbqGje3jdHiV0njQlAW0YvzoahkirFePNQ4RYlfnRQpXZA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@oxc-project/runtime": "0.97.0",
"fdir": "^6.5.0",
@@ -3521,6 +3531,7 @@
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+25 -3
View File
@@ -10,9 +10,31 @@ function App() {
// Logic is now in CurrentPage, App just provides layout
return (
<div className="relative min-h-screen font-sans text-gray-100 overflow-hidden">
{/* Global Background */}
<div className="fixed inset-0 -z-20 bg-gradient-to-br from-deep-sea via-[#1a3c46] to-[#155e55] opacity-100 transition-colors duration-1000"></div>
<div className="fixed inset-0 -z-10 bg-noise opacity-20 brightness-100 contrast-150 pointer-events-none"></div>
{/* Global Background with Gradient and Glow Effects - Similar to image */}
<div className="fixed inset-0 -z-30">
{/* Base Gradient Background - From blue (left) to dark brown (right) with blue gradient */}
<div className="absolute inset-0 bg-gradient-to-r from-[#1a1c2c] via-[#1a1c2c] via-[#1f1f2e] to-[#2d1b1b]"></div>
{/* Blue gradient layer for left side */}
<div className="absolute inset-0 bg-gradient-to-r from-[#1e3a5f]/40 via-[#1a1c2c]/30 to-transparent"></div>
{/* Additional gradient layer for smoother transition */}
<div className="absolute inset-0 bg-gradient-to-r from-[#1a1c2c]/75 via-[#1f1f2e]/55 to-[#2d1b1b]/75"></div>
{/* Vertical gradient for depth */}
<div className="absolute inset-0 bg-gradient-to-b from-[#1a1c2c]/65 via-transparent to-[#2d1b1b]/55"></div>
{/* Glow Effects - Enhanced blue on left */}
{/* Left side - Enhanced Blue glow */}
<div className="absolute top-[-8%] left-[-8%] w-[60%] h-[60%] bg-blue-700/25 blur-[140px] rounded-full animate-float"></div>
<div className="absolute bottom-[5%] left-[5%] w-[45%] h-[45%] bg-blue-800/20 blur-[110px] rounded-full animate-pulse-slow"></div>
<div className="absolute top-[20%] left-[15%] w-[35%] h-[35%] bg-cyan-800/15 blur-[100px] rounded-full animate-float"></div>
{/* Right side - Reddish-orange glow (bottom right prominent like in image) */}
<div className="absolute bottom-[-8%] right-[-8%] w-[55%] h-[55%] bg-orange-800/22 blur-[140px] rounded-full animate-float-delayed"></div>
<div className="absolute top-[15%] right-[10%] w-[40%] h-[40%] bg-amber-800/16 blur-[120px] rounded-full animate-pulse-slow"></div>
{/* Central subtle glow */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[50%] h-[50%] bg-orange-900/12 blur-[160px] rounded-full"></div>
</div>
<div className="fixed inset-0 -z-10 bg-noise opacity-10 brightness-120 contrast-130 pointer-events-none"></div>
{/* Page Content */}
<CurrentPage />
+283
View File
@@ -0,0 +1,283 @@
import React, { useState, useRef, useEffect } from 'react';
import { useStoreData } from '../hooks/useStoreData';
import { Store } from '../utils/store';
import { User, Settings, LogOut, X, Edit2 } from 'lucide-react';
import { GlassCard } from './ui/GlassCard';
import { Button } from './ui/Button';
import clsx from 'clsx';
/**
* 用户资料菜单组件
* 显示用户信息、编辑资料和退出登录选项
*/
export function UserMenu({ isOpen, onClose, onLogout }) {
const data = useStoreData();
const [showEditModal, setShowEditModal] = useState(false);
const menuRef = useRef(null);
// 每次打开菜单时,重置编辑模态框状态(模仿 PncyssD 的逻辑)
// 确保每次打开菜单都显示主菜单界面,而不是编辑界面
useEffect(() => {
if (isOpen) {
// 菜单打开时,确保编辑模态框是关闭的
setShowEditModal(false);
}
}, [isOpen]);
// 点击外部关闭菜单
useEffect(() => {
if (!isOpen) return;
const handleClickOutside = (event) => {
if (menuRef.current && !menuRef.current.contains(event.target)) {
onClose();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [isOpen, onClose]);
// 如果菜单未打开,不渲染任何内容(除非正在显示编辑模态框)
if (!isOpen && !showEditModal) return null;
// 如果正在显示编辑模态框,只渲染编辑模态框
if (showEditModal) {
return (
<EditProfileModal
onClose={() => {
setShowEditModal(false);
// 编辑模态框关闭后,如果菜单是打开的,会显示主菜单
}}
userProfile={data.userProfile}
/>
);
}
// 显示主菜单(isOpen 为 true 且 showEditModal 为 false
// 显示主菜单
return (
<>
{/* 菜单遮罩层 */}
<div
className="fixed inset-0 z-40"
onClick={onClose}
/>
{/* 用户菜单 */}
<div
ref={menuRef}
className="fixed top-24 md:top-4 left-4 md:left-[300px] z-50 w-[calc(100%-2rem)] md:w-80 animate-fade-in"
>
<div className="bg-[#1a1c2c]/95 border border-white/20 shadow-2xl rounded-2xl p-6 space-y-6 backdrop-blur-sm">
{/* 用户信息头部 */}
<div className="flex items-center gap-4 pb-4 border-b border-white/10">
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-primary/30 to-blue-600/30 flex items-center justify-center text-2xl font-bold text-white border border-white/20 shadow-lg">
{data.userProfile.nickname?.[0] || 'U'}
</div>
<div className="flex-1 overflow-hidden">
<div className="font-bold text-gray-100 text-lg truncate">
{data.userProfile.nickname || '旅人'}
</div>
<div className="text-xs text-primary flex items-center gap-1.5 mt-1">
<span className="w-1.5 h-1.5 rounded-full bg-primary shadow-[0_0_5px_rgba(205,133,63,0.5)] animate-pulse"></span>
{data.userProfile.mbti || '未知'} · {data.userProfile.zodiac || '未知'}
</div>
</div>
</div>
{/* 统计数据 */}
<div className="grid grid-cols-2 gap-3">
<div className="p-4 bg-white/5 rounded-xl border border-white/5 text-center">
<div className="text-2xl font-bold text-primary">
{data.lifeTimeline?.length || 0}
</div>
<div className="text-[10px] text-white/40 uppercase tracking-widest mt-1">
生命足迹
</div>
</div>
<div className="p-4 bg-white/5 rounded-xl border border-white/5 text-center">
<div className="text-2xl font-bold text-blue-400">
{data.generatedScripts?.length || 0}
</div>
<div className="text-[10px] text-white/40 uppercase tracking-widest mt-1">
剧本生成
</div>
</div>
</div>
{/* 操作按钮 */}
<div className="space-y-2 pt-2 border-t border-white/5">
<Button
variant="secondary"
size="md"
className="w-full justify-start"
onClick={() => {
setShowEditModal(true);
// 不关闭主菜单,让编辑模态框显示在主菜单之上
// 这样关闭编辑模态框后,主菜单仍然可见
}}
>
<Settings className="w-4 h-4 mr-2" />
编辑资料
</Button>
<Button
variant="ghost"
size="md"
className="w-full justify-start text-red-400 hover:text-red-300 hover:bg-red-500/10"
onClick={() => {
if (window.confirm('确定要退出登录并清除所有数据吗?此操作不可逆。')) {
Store.reset();
if (onLogout) onLogout();
}
}}
>
<LogOut className="w-4 h-4 mr-2" />
退出登录
</Button>
</div>
</div>
</div>
</>
);
}
/**
* 编辑资料模态框组件
*/
function EditProfileModal({ onClose, userProfile }) {
const [formData, setFormData] = useState({
nickname: userProfile.nickname || '',
mbti: userProfile.mbti || '',
zodiac: userProfile.zodiac || '',
hobbies: userProfile.hobbies?.join(', ') || '',
gender: userProfile.gender || 'secret'
});
const [isSaving, setIsSaving] = useState(false);
const handleSave = () => {
setIsSaving(true);
// 更新用户资料
Store.updateProfile({
nickname: formData.nickname,
mbti: formData.mbti,
zodiac: formData.zodiac,
hobbies: formData.hobbies.split(',').map(s => s.trim()).filter(s => s),
gender: formData.gender
});
setTimeout(() => {
setIsSaving(false);
onClose();
}, 300);
};
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-xl">
<GlassCard className="w-full max-w-lg p-8 relative max-h-[90vh] overflow-y-auto">
{/* 关闭按钮 */}
<button
onClick={onClose}
className="absolute top-6 right-6 text-white/40 hover:text-white transition-colors z-10"
>
<X className="w-5 h-5" />
</button>
{/* 标题 */}
<div className="flex items-center gap-4 mb-8">
<div className="w-12 h-12 rounded-xl bg-primary/20 flex items-center justify-center">
<Edit2 className="text-primary w-6 h-6" />
</div>
<div>
<h3 className="text-2xl font-bold text-gray-100">编辑资料</h3>
<p className="text-xs text-gray-400 mt-1">调整你的人生航向基础信息</p>
</div>
</div>
{/* 表单 */}
<div className="space-y-6">
<div>
<label className="text-sm font-medium text-gray-300 mb-2 block">昵称</label>
<input
type="text"
value={formData.nickname}
onChange={(e) => setFormData({ ...formData, nickname: e.target.value })}
placeholder="你想被如何称呼?"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/30 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-gray-300 mb-2 block">MBTI</label>
<input
type="text"
value={formData.mbti}
onChange={(e) => setFormData({ ...formData, mbti: e.target.value })}
placeholder="性格色彩"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/30 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all"
/>
</div>
<div>
<label className="text-sm font-medium text-gray-300 mb-2 block">星座</label>
<input
type="text"
value={formData.zodiac}
onChange={(e) => setFormData({ ...formData, zodiac: e.target.value })}
placeholder="星辰指引"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/30 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all"
/>
</div>
</div>
<div>
<label className="text-sm font-medium text-gray-300 mb-2 block">兴趣爱好</label>
<input
type="text"
value={formData.hobbies}
onChange={(e) => setFormData({ ...formData, hobbies: e.target.value })}
placeholder="让灵魂起舞的事物(用逗号分隔)"
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/30 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all"
/>
</div>
<div>
<label className="text-sm font-medium text-gray-300 mb-2 block">性别</label>
<select
value={formData.gender}
onChange={(e) => setFormData({ ...formData, gender: e.target.value })}
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary/50 transition-all"
>
<option value="secret">保密</option>
<option value="male"></option>
<option value="female"></option>
</select>
</div>
</div>
{/* 操作按钮 */}
<div className="flex gap-4 mt-8 pt-6 border-t border-white/5">
<Button
variant="primary"
size="md"
className="flex-1"
onClick={handleSave}
isLoading={isSaving}
>
保存修改
</Button>
<Button
variant="ghost"
size="md"
onClick={onClose}
>
取消
</Button>
</div>
</GlassCard>
</div>
);
}
+1 -1
View File
@@ -9,7 +9,7 @@ export function GlassCard({ children, className, ...props }) {
return (
<div
className={clsx(
"bg-white/5 backdrop-blur-xl border border-white/10 shadow-2xl rounded-2xl transition-all duration-300",
"bg-white/10 backdrop-blur-xl border border-white/15 shadow-2xl rounded-2xl transition-all duration-300",
className
)}
{...props}
+13 -13
View File
@@ -123,8 +123,8 @@ export function PathView({ onSwitchToScript }) {
return (
<div className="max-w-4xl mx-auto md:p-12 pb-24">
<header className="mb-8">
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-2">
<span className="bg-green-100/10 p-2 rounded-lg text-aurora-green">
<h2 className="text-3xl font-bold text-gold-gradient mb-2 flex items-center gap-2">
<span className="bg-primary/10 p-2 rounded-lg text-primary">
<Map className="w-6 h-6 icon-glow" />
</span>
实现路径
@@ -132,14 +132,14 @@ export function PathView({ onSwitchToScript }) {
<p className="text-gray-400">将幻想落地为行动AI为你定制专属计划</p>
</header>
<div className="p-16 text-center text-gray-400 glass-card rounded-2xl border-dashed border-2 border-white/10">
<Ghost className="w-16 h-16 mx-auto mb-4 opacity-30" />
<p className="mb-4">你需要先有一个剧本才能生成通往它的路径</p>
<div className="p-16 text-center rounded-2xl border border-white/10 bg-[#1a1c2c]/40 backdrop-blur-sm">
<Ghost className="w-16 h-16 mx-auto mb-4 opacity-20 text-gray-500" />
<p className="mb-4 text-gray-400">你需要先有一个剧本才能生成通往它的路径</p>
<button
className="text-primary font-bold hover:underline flex items-center justify-center gap-1 mx-auto"
className="text-primary font-bold hover:text-accent transition-colors flex items-center justify-center gap-1 mx-auto group"
onClick={onSwitchToScript}
>
去生成剧本 <ArrowRight className="w-4 h-4" />
去生成剧本 <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</button>
</div>
</div>
@@ -149,7 +149,7 @@ export function PathView({ onSwitchToScript }) {
return (
<div className="max-w-4xl mx-auto md:p-12 pb-24">
<header className="mb-8">
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-2">
<h2 className="text-3xl font-bold text-gold-gradient mb-2 flex items-center gap-2">
<span className="bg-green-100/10 p-2 rounded-lg text-aurora-green">
<Map className="w-6 h-6 icon-glow" />
</span>
@@ -245,11 +245,11 @@ export function PathView({ onSwitchToScript }) {
onClick={() => setExpandedStep(expandedStep === idx ? null : idx)}
>
<div className="flex items-center gap-5">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-primary to-green-400 text-white flex items-center justify-center font-bold text-lg shadow-lg shadow-primary/30 shrink-0">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-primary to-accent text-white flex items-center justify-center font-bold text-lg shadow-lg shadow-primary/30 shrink-0">
{idx + 1}
</div>
<div>
<h4 className="font-bold text-lg text-white">{step.phase}</h4>
<h4 className="font-bold text-lg text-beige">{step.phase}</h4>
<span className="text-xs text-primary font-medium bg-primary/10 px-2 py-0.5 rounded-full border border-primary/20">
{step.time}
</span>
@@ -277,7 +277,7 @@ export function PathView({ onSwitchToScript }) {
<div
contentEditable={isEditing}
onBlur={(e) => handleStepEdit(idx, 'content', e.currentTarget.textContent)}
className={`p-3 rounded-lg text-sm text-gray-300 leading-relaxed border transition-colors ${
className={`p-3 rounded-lg text-sm text-beige/90 leading-relaxed border transition-colors ${
isEditing
? 'bg-white/10 border-primary/50 ring-2 ring-primary/20'
: 'bg-white/5 border-white/5'
@@ -325,7 +325,7 @@ export function PathView({ onSwitchToScript }) {
</div>
{/* Habits */}
<div>
<h5 className="text-xs font-bold text-green-400 uppercase tracking-wider mb-2 flex items-center gap-1">
<h5 className="text-xs font-bold text-primary uppercase tracking-wider mb-2 flex items-center gap-1">
<Repeat className="w-3 h-3" /> 养成习惯
</h5>
<div
@@ -334,7 +334,7 @@ export function PathView({ onSwitchToScript }) {
className={`p-3 rounded-lg text-xs text-gray-400 leading-relaxed transition-colors ${
isEditing
? 'bg-white/10 border border-primary/50 ring-2 ring-primary/20'
: 'bg-green-500/10 border border-green-500/20'
: 'bg-primary/10 border border-primary/20'
}`}
>
{step.habit}
+22 -22
View File
@@ -81,9 +81,9 @@ export function ScriptView({ onSwitchToPath }) {
return (
<div className="max-w-7xl mx-auto md:p-8 h-full flex flex-col pb-20 md:pb-0">
<header className="mb-8 px-4 md:px-0 pt-4 md:pt-0">
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
<h2 className="text-3xl font-bold text-gold-gradient mb-2 flex items-center gap-3">
<span className="bg-primary/10 p-2 rounded-xl text-primary ring-1 ring-primary/20"><Film className="w-6 h-6 icon-glow" /></span>
<span className="bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400">剧本生成器</span>
<span className="bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400">爽文剧本</span>
</h2>
<p className="text-gray-400 ml-1">基于你的真实画像编织平行时空的无限可能</p>
</header>
@@ -108,7 +108,7 @@ export function ScriptView({ onSwitchToPath }) {
{data.userProfile.nickname ? data.userProfile.nickname[0] : 'U'}
</div>
<div>
<div className="font-bold text-lg text-white">{data.userProfile.nickname || '未命名'}</div>
<div className="font-bold text-lg text-beige">{data.userProfile.nickname || '未命名'}</div>
<div className="text-xs text-primary flex items-center gap-2 mt-1">
<span className="px-2 py-0.5 bg-primary/10 rounded-full border border-primary/20">{data.userProfile.mbti}</span>
<span className="text-gray-400">{data.userProfile.zodiac || '星辰'}</span>
@@ -116,12 +116,12 @@ export function ScriptView({ onSwitchToPath }) {
</div>
</div>
<div className="space-y-3">
<div className="text-xs text-gray-300 bg-black/20 p-3 rounded-xl border border-white/5">
<span className="text-primary font-bold block mb-1">天赋</span>
<div className="text-xs text-beige/90 bg-black/20 p-3 rounded-xl border border-white/5">
<span className="text-gold-gradient font-bold block mb-1">天赋</span>
{(data.userProfile.hobbies || []).join(' / ') || '暂无'}
</div>
<div className="text-xs text-gray-300 bg-black/20 p-3 rounded-xl border border-white/5">
<span className="text-primary font-bold block mb-1">愿景</span>
<div className="text-xs text-beige/90 bg-black/20 p-3 rounded-xl border border-white/5">
<span className="text-gold-gradient font-bold block mb-1">愿景</span>
<span className="line-clamp-2">{data.userProfile.futureVision || '暂无'}</span>
</div>
</div>
@@ -130,7 +130,7 @@ export function ScriptView({ onSwitchToPath }) {
{/* Input Form */}
<Card className="p-5">
<label className="block text-sm font-bold text-gray-300 mb-2 flex items-center gap-2">
<label className="block text-sm font-bold text-beige/90 mb-2 flex items-center gap-2">
<span className="w-5 h-5 rounded-full bg-white/10 flex items-center justify-center text-xs">1</span>
你的渴望主题
</label>
@@ -170,7 +170,7 @@ export function ScriptView({ onSwitchToPath }) {
variant="primary"
onClick={handleGenerate}
disabled={loading}
className="w-full py-3 shadow-[0_0_20px_rgba(16,185,129,0.2)] flex justify-center items-center gap-2 group"
className="w-full py-3 shadow-[0_0_20px_rgba(205,133,63,0.2)] flex justify-center items-center gap-2 group"
>
{loading ? <><Loader className="animate-spin w-4 h-4" /> 编织命运中...</> : <><Sparkles className="w-4 h-4 group-hover:rotate-12 transition-transform" /> 生成平行人生</>}
</Button>
@@ -197,11 +197,11 @@ export function ScriptView({ onSwitchToPath }) {
className={clsx(
"group relative p-3 rounded-xl cursor-pointer transition-all border",
selectedScriptId === s.id
? "bg-primary/10 border-primary/30 shadow-[inset_0_0_10px_rgba(16,185,129,0.05)]"
? "bg-primary/10 border-primary/30 shadow-[inset_0_0_10px_rgba(205,133,63,0.05)]"
: "bg-transparent border-transparent hover:bg-white/5 hover:border-white/10"
)}
>
<div className={clsx("font-bold text-sm truncate mb-1 pr-6 transition-colors", selectedScriptId === s.id ? "text-primary" : "text-gray-300 group-hover:text-white")}>
<div className={clsx("font-bold text-sm truncate mb-1 pr-6 transition-colors", selectedScriptId === s.id ? "text-primary" : "text-beige/90 group-hover:text-beige")}>
{s.title}
</div>
<div className="flex justify-between items-center">
@@ -233,8 +233,8 @@ export function ScriptView({ onSwitchToPath }) {
<div className="w-24 h-24 rounded-full bg-white/5 flex items-center justify-center mb-6 animate-pulse-slow ring-1 ring-white/10">
<Film className="w-10 h-10 opacity-40" />
</div>
<p className="text-xl mb-2 font-medium text-gray-300">舞台已就绪</p>
<p className="text-sm opacity-60">请在左侧输入设定生成你的平行人生</p>
<p className="text-xl mb-2 font-medium text-beige/90">舞台已就绪</p>
<p className="text-sm opacity-60">请在左侧设定需求开启你的爽文人生</p>
</div>
) : (
<Card className="h-full overflow-hidden flex flex-col relative group bg-black/20 backdrop-blur-xl border-white/10">
@@ -251,11 +251,11 @@ export function ScriptView({ onSwitchToPath }) {
<div className="flex-1 overflow-y-auto custom-scrollbar p-6 md:p-12">
<div className="text-center mb-12 border-b border-white/10 pb-8">
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gradient-to-r from-primary/20 to-accent/20 text-primary text-xs font-bold rounded-full mb-6 tracking-widest uppercase border border-primary/20 shadow-[0_0_10px_rgba(16,185,129,0.1)]">
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gradient-to-r from-primary/20 via-accent/20 to-aurora-green/20 text-gold-gradient text-xs font-bold rounded-full mb-6 tracking-widest uppercase border border-primary/20 shadow-[0_0_10px_rgba(205,133,63,0.1)]">
<Stars className="w-3 h-3" />
{selectedScript.style === 'career' ? '职场逆袭' : selectedScript.style === 'love' ? '情感圆满' : '玄幻觉醒'}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white leading-tight mb-4 tracking-tight drop-shadow-lg">{selectedScript.title}</h2>
<h2 className="text-3xl md:text-5xl font-bold text-gold-gradient leading-tight mb-4 tracking-tight drop-shadow-lg">{selectedScript.title}</h2>
<div className="text-sm text-gray-500 flex items-center justify-center gap-2">
<span>生成于 {new Date(selectedScript.createdAt).toLocaleDateString()}</span>
<span>·</span>
@@ -275,7 +275,7 @@ export function ScriptView({ onSwitchToPath }) {
<div className="w-10 h-10 rounded-full bg-white/10 text-primary font-bold flex items-center justify-center border border-white/10 shadow-[0_0_10px_rgba(0,0,0,0.2)] z-10 shrink-0 text-lg font-serif">1</div>
<h4 className="font-bold text-gray-100 text-xl group-hover/chapter:text-primary transition-colors">序幕低谷回响</h4>
</div>
<div className="md:pl-14 text-gray-300 leading-loose text-justify text-lg font-light tracking-wide">
<div className="md:pl-14 text-beige/90 leading-loose text-justify text-lg font-light tracking-wide">
{selectedScript.plot.intro}
</div>
</div>
@@ -283,11 +283,11 @@ export function ScriptView({ onSwitchToPath }) {
{/* Chapter 2 */}
<div className="mb-12 relative group/chapter">
<div className="flex items-center gap-4 mb-4">
<div className="w-10 h-10 rounded-full bg-primary/20 text-primary font-bold flex items-center justify-center border border-primary/20 shadow-[0_0_15px_rgba(16,185,129,0.1)] z-10 shrink-0 text-lg font-serif">2</div>
<h4 className="font-bold text-white text-xl group-hover/chapter:text-primary transition-colors">转折契机出现</h4>
<div className="w-10 h-10 rounded-full bg-primary/20 text-primary font-bold flex items-center justify-center border border-primary/20 shadow-[0_0_15px_rgba(205,133,63,0.1)] z-10 shrink-0 text-lg font-serif">2</div>
<h4 className="font-bold text-beige text-xl group-hover/chapter:text-primary transition-colors">转折契机出现</h4>
</div>
<div className="md:pl-14">
<div className="text-gray-200 leading-loose text-justify p-6 bg-gradient-to-b from-white/5 to-transparent rounded-2xl border border-white/5 text-lg font-light tracking-wide shadow-inner">
<div className="text-beige/95 leading-loose text-justify p-6 bg-gradient-to-b from-white/5 to-transparent rounded-2xl border border-white/5 text-lg font-light tracking-wide shadow-inner">
{selectedScript.plot.turning}
</div>
</div>
@@ -297,9 +297,9 @@ export function ScriptView({ onSwitchToPath }) {
<div className="mb-12 relative group/chapter">
<div className="flex items-center gap-4 mb-4">
<div className="w-10 h-10 rounded-full bg-accent/20 text-accent font-bold flex items-center justify-center border border-accent/20 shadow-[0_0_15px_rgba(255,165,0,0.1)] z-10 shrink-0 text-lg font-serif">3</div>
<h4 className="font-bold text-white text-xl group-hover/chapter:text-accent transition-colors">高潮命运抉择</h4>
<h4 className="font-bold text-beige text-xl group-hover/chapter:text-accent transition-colors">高潮命运抉择</h4>
</div>
<div className="md:pl-14 text-gray-300 leading-loose text-justify text-lg font-light tracking-wide">
<div className="md:pl-14 text-beige/90 leading-loose text-justify text-lg font-light tracking-wide">
{selectedScript.plot.climax}
</div>
</div>
@@ -310,7 +310,7 @@ export function ScriptView({ onSwitchToPath }) {
<div className="w-10 h-10 rounded-full bg-white/10 text-gray-400 font-bold flex items-center justify-center border border-white/10 z-10 shrink-0 text-lg font-serif">4</div>
<h4 className="font-bold text-gray-100 text-xl group-hover/chapter:text-gray-300 transition-colors">结局新的开始</h4>
</div>
<div className="md:pl-14 text-gray-300 leading-loose text-justify text-lg font-light tracking-wide">
<div className="md:pl-14 text-beige/90 leading-loose text-justify text-lg font-light tracking-wide">
{selectedScript.plot.ending}
</div>
</div>
@@ -44,11 +44,11 @@ export function TimelineView() {
return (
<div className="max-w-4xl mx-auto md:p-12 pb-24">
<header className="mb-8 md:mb-12">
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-2">
<h2 className="text-3xl font-bold text-gold-gradient mb-2 flex items-center gap-2">
<span className="bg-primary/20 p-2 rounded-lg text-primary"><BookHeart className="w-6 h-6 icon-glow" /></span>
时空日记
人生轨迹
</h2>
<p className="text-gray-400 text-sm md:text-base">记录每一个当下让AI为你照见未来</p>
<p className="text-gray-400 text-sm md:text-base">塑造你的每一刻都被星辰见证</p>
</header>
<Card className="p-5 md:p-8 mb-12 relative overflow-hidden group hover:shadow-xl border-l-4 border-primary">
@@ -83,7 +83,7 @@ export function TimelineView() {
{events.length === 0 ? (
<div className="text-center text-gray-400 py-20 bg-white/5 rounded-3xl border border-dashed border-white/10 relative z-10">
<PenTool className="w-12 h-12 mx-auto mb-4 opacity-30 text-primary" />
<p>暂无记录写下你的第一篇日记吧</p>
<p>执笔写下哪些经历让你成为了现在的自己</p>
</div>
) : (
events.map((item, index) => (
@@ -99,16 +99,16 @@ export function TimelineView() {
</div>
{/* Center Dot */}
<div className="absolute left-6 md:left-1/2 -translate-x-1/2 w-4 h-4 rounded-full border-4 border-deep-sea bg-primary z-20 shadow-[0_0_15px_rgba(42,157,143,0.5)] group-hover:scale-125 transition-transform order-2"></div>
<div className="absolute left-6 md:left-1/2 -translate-x-1/2 w-4 h-4 rounded-full border-4 border-deep-sea bg-primary z-20 shadow-[0_0_15px_rgba(205,133,63,0.5)] group-hover:scale-125 transition-transform order-2"></div>
{/* Content Card */}
<div className={`md:w-5/12 pl-12 md:pl-0 order-3 ${index % 2 === 1 ? 'md:order-1 md:text-right md:pr-10' : 'md:pl-10'}`}>
<Card className="p-5 hover:-translate-y-1 transition-transform relative overflow-hidden">
<div className="absolute top-0 right-0 p-3 opacity-10">
<Quote className="w-8 h-8 text-white" />
<Quote className="w-8 h-8 text-beige" />
</div>
<h3 className="font-bold text-lg text-white mb-2">{item.title}</h3>
<p className="text-gray-300 text-sm leading-relaxed mb-4">{item.content}</p>
<h3 className="font-bold text-lg text-beige mb-2">{item.title}</h3>
<p className="text-beige/90 text-sm leading-relaxed mb-4">{item.content}</p>
{item.aiReply && (
<div className="bg-primary/10 rounded-xl p-3 text-sm text-primary/90 flex gap-3 border border-primary/10">
+38 -16
View File
@@ -1,11 +1,16 @@
@import "tailwindcss";
@theme {
--color-primary: #2A9D8F;
--color-secondary: #264653;
--color-accent: #E9C46A;
--color-aurora-green: #4CC9F0;
--color-deep-sea: #0f1c2e;
--color-primary: #CD853F;
--color-secondary: #0a0c10;
--color-accent: #DAA520;
--color-aurora-green: #FFD700;
--color-deep-sea: #1a1c2c;
--color-text-beige: #E8D5B7;
--color-text-warm: #E8D5B7;
--color-gold-dark: #CD853F;
--color-gold-medium: #DAA520;
--color-gold-light: #FFD700;
--font-sans: "Noto Sans SC", sans-serif;
@@ -30,9 +35,12 @@
100% { transform: translateY(0); opacity: 1; }
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(5%, 5%) scale(1.1); }
}
.animate-float { animation: float 15s infinite ease-in-out; }
.animate-float-delayed { animation: float 20s infinite ease-in-out reverse; }
@keyframes breathe {
0%, 100% { opacity: 0.8; transform: scale(1); }
50% { opacity: 1; transform: scale(1.02); }
@@ -42,9 +50,9 @@
to { transform: rotate(360deg); }
}
@keyframes pulse-ring {
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(42, 157, 143, 0.7); }
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(42, 157, 143, 0); }
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(42, 157, 143, 0); }
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 171, 145, 0.7); }
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(255, 171, 145, 0); }
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 171, 145, 0); }
}
@keyframes audio-wave {
0%, 100% { height: 5px; }
@@ -55,7 +63,7 @@
body {
-webkit-tap-highlight-color: transparent;
font-family: 'Noto Sans SC', sans-serif;
@apply bg-deep-sea text-gray-800 overflow-x-hidden selection:bg-primary selection:text-white;
@apply bg-deep-sea text-gray-100 overflow-x-hidden selection:bg-primary/30 selection:text-white;
}
/* Background Noise Utility */
@@ -76,14 +84,14 @@ body {
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(42, 157, 143, 0.6);
background: rgba(255, 171, 145, 0.6);
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(42, 157, 143, 0.4);
background: rgba(255, 171, 145, 0.4);
}
/* Transitions */
@@ -128,8 +136,8 @@ body {
}
.input-field:focus {
border-color: #2A9D8F;
box-shadow: 0 0 0 4px rgba(42, 157, 143, 0.15);
border-color: #CD853F;
box-shadow: 0 0 0 4px rgba(205, 133, 63, 0.15);
background: white;
}
@@ -140,7 +148,7 @@ body {
}
.input-field-dark:focus {
background: rgba(255, 255, 255, 0.1);
border-color: #2A9D8F;
border-color: #CD853F;
}
select.input-field {
@@ -172,6 +180,20 @@ select.input-field-dark option {
.audio-waves span:nth-child(3) { animation-delay: 0.2s; }
.audio-waves span:nth-child(4) { animation-delay: 0.3s; }
/* Text Beige Color Utility - 浅米色文字 */
.text-beige {
color: #E8D5B7;
}
/* Advanced Gold Gradient Text - 高级金色渐变文字(更白亮、更明显、降低饱和度) */
.text-gold-gradient {
background: linear-gradient(135deg, #E8D5B7 0%, #F4D03F 30%, #FFD700 60%, #FFF8DC 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: brightness(1.1) saturate(0.9);
}
/* Utilities */
.scrollbar-hide::-webkit-scrollbar {
display: none;
+45 -22
View File
@@ -5,6 +5,7 @@ import { AudioEngine } from '../utils/audioEngine';
import { TimelineView } from '../components/views/TimelineView';
import { ScriptView } from '../components/views/ScriptView';
import { PathView } from '../components/views/PathView';
import { UserMenu } from '../components/UserMenu';
import {
Compass,
BookOpen,
@@ -24,6 +25,7 @@ export function DashboardPage() {
const [activeTab, setActiveTab] = useState('timeline');
const [isMusicPlaying, setIsMusicPlaying] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
// Initialize Audio Engine state based on store
useEffect(() => {
@@ -55,32 +57,40 @@ export function DashboardPage() {
className={clsx(
"w-full text-left px-4 py-3 rounded-xl flex items-center gap-3 transition-all duration-300 relative overflow-hidden group",
activeTab === tab
? "bg-primary/20 text-primary font-bold shadow-[0_0_15px_rgba(16,185,129,0.1)] border border-primary/20"
: "text-gray-400 hover:text-white hover:bg-white/5 border border-transparent"
? "bg-primary/20 text-primary font-bold shadow-[0_0_15px_rgba(205,133,63,0.1)] border border-primary/20"
: "text-gray-400 hover:text-primary hover:bg-white/5 border border-transparent"
)}
>
<Icon className={clsx("w-5 h-5 transition-transform", activeTab === tab ? "text-primary" : "text-gray-400 group-hover:text-white", activeTab !== tab && "group-hover:scale-110")} />
<Icon className={clsx("w-5 h-5 transition-transform", activeTab === tab ? "text-primary" : "text-gray-400 group-hover:text-primary", activeTab !== tab && "group-hover:scale-110")} />
<span className="hidden md:inline">{label}</span>
<span className="md:hidden">{mobileLabel || label}</span>
{activeTab === tab && (
<div className="absolute right-0 top-0 bottom-0 w-1 bg-primary shadow-[0_0_10px_#10b981]"></div>
<div className="absolute right-0 top-0 bottom-0 w-1 bg-primary shadow-[0_0_10px_rgba(205,133,63,0.8)]"></div>
)}
</button>
);
return (
<div className="min-h-screen flex flex-col md:flex-row transition-all duration-500 font-sans text-gray-100 bg-deep-sea overflow-hidden">
{/* Ambient Background */}
{/* Ambient Background - Additional Layer for Dashboard with Blue Gradient */}
<div className="fixed inset-0 pointer-events-none z-0">
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_50%_50%,rgba(16,185,129,0.05),transparent_50%)]"></div>
<div className="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-primary/5 rounded-full blur-[120px] animate-pulse-slow"></div>
{/* Blue gradient overlay on left */}
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_20%_50%,rgba(30,58,95,0.15),transparent_60%)]"></div>
{/* Subtle radial gradient overlay */}
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_30%_50%,rgba(205,133,63,0.08),transparent_70%)]"></div>
<div className="absolute top-0 right-0 w-full h-full bg-[radial-gradient(circle_at_70%_50%,rgba(255,140,0,0.06),transparent_65%)]"></div>
{/* Blue glow effects on left */}
<div className="absolute top-[20%] left-[15%] w-[40%] h-[40%] bg-blue-700/10 rounded-full blur-[120px] animate-pulse-slow"></div>
<div className="absolute bottom-[20%] left-[10%] w-[35%] h-[35%] bg-cyan-800/8 rounded-full blur-[100px] animate-float"></div>
{/* Subtle glow effects on right */}
<div className="absolute top-[25%] right-[25%] w-[35%] h-[35%] bg-orange-800/8 rounded-full blur-[120px] animate-pulse-slow"></div>
</div>
{/* Mobile Header */}
<div className="md:hidden bg-black/20 backdrop-blur-xl border-b border-white/10 p-4 flex justify-between items-center z-50 relative">
<div className="md:hidden bg-black/15 backdrop-blur-xl border-b border-white/10 p-4 flex justify-between items-center z-50 relative">
<div className="flex items-center gap-2 font-bold text-lg">
<Compass className="text-primary w-6 h-6 animate-spin-slow" />
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-accent">人生轨迹</span>
<span className="text-gold-gradient">人生OS</span>
</div>
<button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}>
{isMobileMenuOpen ? <X className="text-white" /> : <Menu className="text-white" />}
@@ -89,47 +99,50 @@ export function DashboardPage() {
{/* Sidebar (Desktop) / Drawer (Mobile) */}
<nav className={clsx(
"bg-black/20 backdrop-blur-xl border-r border-white/10 w-full md:w-72 flex-shrink-0 flex flex-col justify-between z-40 fixed md:relative h-[calc(100vh-64px)] md:h-screen top-16 md:top-0 left-0 transition-transform duration-300",
"bg-black/15 backdrop-blur-xl border-r border-white/10 w-full md:w-72 flex-shrink-0 flex flex-col justify-between z-40 fixed md:relative h-[calc(100vh-64px)] md:h-screen top-16 md:top-0 left-0 transition-transform duration-300",
isMobileMenuOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"
)}>
{/* Background Decoration */}
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-transparent to-primary/5 pointer-events-none"></div>
<div className="p-6 overflow-y-auto relative z-10">
<h1 className="hidden md:flex text-2xl font-bold tracking-wider mb-8 items-center gap-3 text-transparent bg-clip-text bg-gradient-to-r from-primary to-white">
<Compass className="text-primary stroke-2 animate-spin-slow" /> 人生轨迹
<h1 className="hidden md:flex text-2xl font-bold tracking-wider mb-8 items-center gap-3 text-gold-gradient">
<Compass className="text-primary stroke-2 animate-spin-slow" /> 人生OS
</h1>
{/* User Card */}
<div className="flex items-center gap-4 mb-8 p-4 bg-white/5 rounded-2xl backdrop-blur-sm border border-white/10 hover:bg-white/10 transition-colors cursor-default group">
<button
onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
className="flex items-center gap-4 mb-8 p-4 bg-white/5 rounded-2xl backdrop-blur-sm border border-white/10 hover:bg-white/10 transition-colors cursor-pointer group w-full text-left"
>
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-primary to-blue-600 flex items-center justify-center text-white font-bold text-xl shadow-inner relative overflow-hidden group-hover:scale-105 transition-transform shrink-0 border border-white/20">
{data.userProfile.nickname?.[0] || 'U'}
<div className="absolute inset-0 bg-white/20 opacity-0 group-hover:opacity-100 transition-opacity"></div>
</div>
<div className="overflow-hidden">
<div className="font-bold text-white truncate">{data.userProfile.nickname || '旅人'}</div>
<div className="overflow-hidden flex-1">
<div className="font-bold text-gray-100 truncate">{data.userProfile.nickname || '旅人'}</div>
<div className="text-xs text-primary flex items-center gap-1.5 mt-0.5">
<span className="w-1.5 h-1.5 rounded-full bg-primary shadow-[0_0_5px_rgba(16,185,129,0.5)] animate-pulse"></span>
{data.userProfile.mbti} · {data.userProfile.zodiac || '未知'}
</div>
<span className="w-1.5 h-1.5 rounded-full bg-primary shadow-[0_0_5px_rgba(205,133,63,0.5)] animate-pulse"></span>
{data.userProfile.mbti || '未知'} · {data.userProfile.zodiac || '未知'}
</div>
</div>
</button>
{/* Navigation */}
<div className="space-y-2">
<NavButton tab="timeline" icon={BookOpen} label="时空日记" mobileLabel="日记" />
<NavButton tab="script" icon={Film} label="剧本生成器" mobileLabel="剧本" />
<NavButton tab="timeline" icon={BookOpen} label="人生轨迹" mobileLabel="轨迹" />
<NavButton tab="script" icon={Film} label="爽文剧本" mobileLabel="剧本" />
<NavButton tab="path" icon={Map} label="实现路径" mobileLabel="路径" />
</div>
</div>
<div className="p-6 text-xs text-gray-500 border-t border-white/5 space-y-4 bg-black/40 backdrop-blur-md md:bg-transparent relative z-10">
<div className="p-6 text-xs text-gray-500 border-t border-white/5 space-y-4 bg-black/20 backdrop-blur-md md:bg-transparent relative z-10">
{/* Music Player */}
<button
onClick={handleMusicToggle}
className={clsx(
"flex items-center justify-between w-full px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10 transition-all border group",
isMusicPlaying ? "border-primary/30 shadow-[0_0_10px_rgba(16,185,129,0.1)]" : "border-white/5"
isMusicPlaying ? "border-primary/30 shadow-[0_0_10px_rgba(205,133,63,0.1)]" : "border-white/5"
)}
>
<div className="flex items-center gap-3 text-gray-300">
@@ -163,6 +176,16 @@ export function DashboardPage() {
{activeTab === 'script' && <ScriptView onSwitchToPath={() => setActiveTab('path')} />}
{activeTab === 'path' && <PathView onSwitchToScript={() => setActiveTab('script')} />}
</main>
{/* User Menu */}
<UserMenu
isOpen={isUserMenuOpen}
onClose={() => setIsUserMenuOpen(false)}
onLogout={() => {
// 退出登录后,App.jsx 会检测到 onboardingComplete 变为 false,自动跳转到登录页
window.location.reload();
}}
/>
</div>
);
}
+2 -2
View File
@@ -93,7 +93,7 @@ export function LandingPage({ onStart }) {
</div>
<h1 className="text-4xl md:text-6xl font-bold text-white tracking-tight landing-title">
人生轨迹
人生OS
<span className="block text-xl md:text-2xl font-light mt-4 text-primary/80">Life Trajectory</span>
</h1>
@@ -113,7 +113,7 @@ export function LandingPage({ onStart }) {
variant="primary"
size="lg"
onClick={() => setShowLoginModal(true)}
className="w-full shadow-[0_0_20px_rgba(16,185,129,0.3)] hover:shadow-[0_0_30px_rgba(16,185,129,0.5)] transition-all"
className="w-full shadow-[0_0_20px_rgba(205,133,63,0.3)] hover:shadow-[0_0_30px_rgba(205,133,63,0.5)] transition-all"
>
登录账号
</Button>
+1 -1
View File
@@ -171,7 +171,7 @@ export function OnboardingPage({ onFinish }) {
{[0,1,2,3,4].map(i => (
<div key={i} className={clsx(
"h-1 rounded-full transition-all duration-500",
i <= step ? 'w-8 bg-primary shadow-[0_0_10px_rgba(42,157,143,0.5)]' : 'w-2 bg-white/10'
i <= step ? 'w-8 bg-primary shadow-[0_0_10px_rgba(205,133,63,0.5)]' : 'w-2 bg-white/10'
)}></div>
))}
</div>
+2 -1
View File
@@ -9,7 +9,8 @@ export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:19089',
// target: 'http://localhost:19089',
target: 'http://101.200.208.45:19089',
changeOrigin: true,
}
}