Files
happy-life-star/life-script/src/views/ProfileModal.jsx
T

255 lines
8.5 KiB
React
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 { useState, useEffect } from 'react';
import { Settings, Settings2 } from 'lucide-react';
import Modal from '../components/Modal';
import { GlassButton, GlassInput, GlassSelect } from '../components/ui';
import useStore from '../store/useStore';
import * as dictionaryService from '../services/dictionary';
/**
* ProfileModal 组件
* 用户资料模态框,支持查看和编辑模式
* @param {Object} props
* @param {boolean} props.isOpen - 是否打开
* @param {Function} props.onClose - 关闭回调
*/
const ProfileModal = ({ isOpen, onClose }) => {
const { registrationData, lifeEvents, scripts, updateRegistration, saveUserProfile, clear } = useStore();
// 编辑模式状态
const [isEditing, setIsEditing] = useState(false);
const [isSaving, setIsSaving] = useState(false);
// 字典数据状态
const [zodiacOptions, setZodiacOptions] = useState([]);
const [mbtiOptions, setMbtiOptions] = useState([]);
const [genderOptions, setGenderOptions] = useState([]);
// 编辑表单状态
const [editForm, setEditForm] = useState({
nickname: registrationData.nickname,
profession: registrationData.profession || '',
gender: registrationData.gender || '',
mbti: registrationData.mbti,
zodiac: registrationData.zodiac,
hobbies: registrationData.hobbies?.join('') || ''
});
// 同步 registrationData 到 editForm
useEffect(() => {
setEditForm({
nickname: registrationData.nickname,
profession: registrationData.profession || '',
gender: registrationData.gender || '',
mbti: registrationData.mbti,
zodiac: registrationData.zodiac,
hobbies: registrationData.hobbies?.join('') || ''
});
}, [registrationData]);
// 加载字典数据
useEffect(() => {
const loadDictionaries = async () => {
try {
const [zodiacList, mbtiList, genderList] = await Promise.all([
dictionaryService.getZodiacList(),
dictionaryService.getMbtiList(),
dictionaryService.getGenderList()
]);
setZodiacOptions([{ value: '', label: '请选择星座' }, ...dictionaryService.transformToOptions(zodiacList)]);
setMbtiOptions([{ value: '', label: '请选择MBTI' }, ...dictionaryService.transformToOptions(mbtiList)]);
setGenderOptions([{ value: '', label: '请选择性别' }, ...dictionaryService.transformToOptions(genderList)]);
} catch (error) {
console.error('加载字典数据失败:', error);
setZodiacOptions([{ value: '', label: '请选择星座' }]);
setMbtiOptions([{ value: '', label: '请选择MBTI' }]);
setGenderOptions([{ value: '', label: '请选择性别' }]);
}
};
if (isOpen) {
loadDictionaries();
}
}, [isOpen]);
/**
* 处理保存
*/
const handleSave = async () => {
setIsSaving(true);
try {
updateRegistration({
nickname: editForm.nickname,
profession: editForm.profession,
gender: editForm.gender,
mbti: editForm.mbti,
zodiac: editForm.zodiac,
hobbies: editForm.hobbies.split(/[,]/).map(s => s.trim()).filter(s => s)
});
await saveUserProfile();
setIsEditing(false);
} catch (error) {
console.error('保存失败:', error);
// 即使后端保存失败,本地已更新
setIsEditing(false);
} finally {
setIsSaving(false);
}
};
/**
* 处理取消编辑
*/
const handleCancel = () => {
setEditForm({
nickname: registrationData.nickname,
profession: registrationData.profession || '',
gender: registrationData.gender || '',
mbti: registrationData.mbti,
zodiac: registrationData.zodiac,
hobbies: registrationData.hobbies?.join('') || ''
});
setIsEditing(false);
};
/**
* 处理清除数据
*/
const handleClear = () => {
if (confirm('确定要删除所有记录吗?此操作不可逆。')) {
clear();
}
};
/**
* 渲染查看模式
*/
const renderViewMode = () => (
<div className="animate-fade-in space-y-8">
{/* 用户头像和基本信息 */}
<div className="flex items-center gap-6">
<div className="w-20 h-20 rounded-3xl bg-gradient-to-br from-orange-400/20 to-orange-600/20 flex items-center justify-center text-3xl border border-white/10">
{(registrationData.nickname || '人').charAt(0)}
</div>
<div>
<h4 className="text-2xl font-serif text-white/90">
{registrationData.nickname || '旅行者'}
</h4>
<p className="text-[10px] text-white/30 uppercase tracking-[0.2em] mt-1">
{registrationData.mbti || '-'} | {registrationData.zodiac || '-'}
</p>
</div>
</div>
{/* 统计数据 */}
<div className="grid grid-cols-2 gap-4">
<div className="p-4 bg-white/[0.02] rounded-2xl border border-white/5 text-center">
<div className="text-lg font-serif text-orange-200">{lifeEvents.length}</div>
<div className="text-[9px] text-white/30 uppercase tracking-widest mt-1">生命足迹</div>
</div>
<div className="p-4 bg-white/[0.02] rounded-2xl border border-white/5 text-center">
<div className="text-lg font-serif text-blue-200">{scripts.length}</div>
<div className="text-[9px] text-white/30 uppercase tracking-widest mt-1">天命卷轴</div>
</div>
</div>
{/* 操作按钮 */}
<div className="space-y-4 pt-4 border-t border-white/5">
<GlassButton
onClick={() => setIsEditing(true)}
className="w-full py-4 text-sm font-bold flex gap-3 items-center justify-center"
>
<Settings className="w-4 h-4" /> 编辑资料
</GlassButton>
<button
onClick={handleClear}
className="w-full py-4 text-[10px] text-red-400/40 hover:text-red-400 uppercase tracking-widest transition-colors"
>
清除数据并退出
</button>
</div>
</div>
);
/**
* 渲染编辑模式
*/
const renderEditMode = () => (
<div className="space-y-6 animate-fade-in">
{/* 编辑标题 */}
<div className="flex items-center gap-4 mb-8">
<div className="w-12 h-12 rounded-full bg-orange-200/10 flex items-center justify-center">
<Settings2 className="text-orange-200 w-5 h-5" />
</div>
<div>
<h4 className="text-xl font-serif">个人设定</h4>
<p className="text-xs text-white/40">在这里调整你的人生航向基础信息</p>
</div>
</div>
{/* 编辑表单 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-6">
<GlassInput
label="昵称"
placeholder="你想被如何称呼?"
value={editForm.nickname}
onChange={(v) => setEditForm(prev => ({ ...prev, nickname: v }))}
/>
<GlassInput
label="职业"
placeholder="你当下的社会锚点"
value={editForm.profession}
onChange={(v) => setEditForm(prev => ({ ...prev, profession: v }))}
/>
<GlassSelect
label="性别"
options={genderOptions}
value={editForm.gender}
onChange={(v) => setEditForm(prev => ({ ...prev, gender: v }))}
/>
<GlassSelect
label="MBTI"
options={mbtiOptions}
value={editForm.mbti}
onChange={(v) => setEditForm(prev => ({ ...prev, mbti: v }))}
/>
<GlassSelect
label="星座"
options={zodiacOptions}
value={editForm.zodiac}
onChange={(v) => setEditForm(prev => ({ ...prev, zodiac: v }))}
/>
</div>
<GlassInput
label="兴趣爱好"
placeholder="让灵魂起舞的事物,用逗号分隔"
value={editForm.hobbies}
onChange={(v) => setEditForm(prev => ({ ...prev, hobbies: v }))}
/>
{/* 操作按钮 */}
<div className="flex gap-4 mt-8 pt-6 border-t border-white/5">
<GlassButton
onClick={handleSave}
loading={isSaving}
className="flex-1 py-3 bg-orange-200/10 text-orange-100 font-bold tracking-widest"
>
保存修改
</GlassButton>
<GlassButton
onClick={handleCancel}
className="px-6 py-3 text-white/40"
>
返回
</GlassButton>
</div>
</div>
);
return (
<Modal isOpen={isOpen} onClose={onClose}>
{isEditing ? renderEditMode() : renderViewMode()}
</Modal>
);
};
export default ProfileModal;