255 lines
8.5 KiB
React
255 lines
8.5 KiB
React
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;
|