// src/views/Settings/SettingsPage.js import React, { useState } from 'react'; import { Box, Container, VStack, HStack, Text, Heading, Button, Input, FormControl, FormLabel, Card, CardBody, CardHeader, useToast, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, ModalCloseButton, useDisclosure, Badge, Tabs, TabList, TabPanels, Tab, TabPanel, PinInput, PinInputField } from '@chakra-ui/react'; import { LinkIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons'; import { FaWeixin, FaMobile, FaEnvelope } from 'react-icons/fa'; import { useAuth } from '../../contexts/AuthContext'; import { getApiBase } from '../../utils/apiConfig'; import { logger } from '../../utils/logger'; import { useProfileEvents } from '../../hooks/useProfileEvents'; export default function SettingsPage() { const { user, updateUser } = useAuth(); const toast = useToast(); // 深色模式固定颜色(Settings 页面始终使用深色主题) const headingColor = 'white'; const textColor = 'gray.100'; const subTextColor = 'gray.400'; const cardBg = 'gray.800'; const borderColor = 'gray.600'; // 🎯 初始化设置页面埋点Hook const profileEvents = useProfileEvents({ pageType: 'settings' }); // 模态框状态 const { isOpen: isPhoneOpen, onOpen: onPhoneOpen, onClose: onPhoneClose } = useDisclosure(); const { isOpen: isEmailOpen, onOpen: onEmailOpen, onClose: onEmailClose } = useDisclosure(); // 表单状态 const [isLoading, setIsLoading] = useState(false); const [phoneForm, setPhoneForm] = useState({ phone: '', verificationCode: '' }); const [emailForm, setEmailForm] = useState({ email: '', verificationCode: '' }); // 发送验证码 const sendVerificationCode = async (type) => { setIsLoading(true); try { if (type === 'phone') { const url = '/api/account/phone/send-code'; logger.api.request('POST', url, { phone: phoneForm.phone.substring(0, 3) + '****' }); const res = await fetch(getApiBase() + url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ phone: phoneForm.phone }) }); const data = await res.json(); logger.api.response('POST', url, res.status, data); if (!res.ok) throw new Error(data.error || '发送失败'); } else { const url = '/api/account/email/send-bind-code'; logger.api.request('POST', url, { email: emailForm.email.substring(0, 3) + '***@***' }); // 使用绑定邮箱的验证码API const res = await fetch(getApiBase() + url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ email: emailForm.email }) }); const data = await res.json(); logger.api.response('POST', url, res.status, data); if (!res.ok) throw new Error(data.error || '发送失败'); } // ❌ 移除验证码发送成功toast logger.info('SettingsPage', `${type === 'phone' ? '短信' : '邮件'}验证码已发送`); } catch (error) { logger.error('SettingsPage', 'sendVerificationCode', error, { type }); toast({ title: "发送失败", description: error.message, status: "error", duration: 3000, isClosable: true, }); } finally { setIsLoading(false); } }; // 绑定手机号 const handlePhoneBind = async () => { setIsLoading(true); try { const res = await fetch(getApiBase() + '/api/account/phone/bind', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ phone: phoneForm.phone, code: phoneForm.verificationCode }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || '绑定失败'); updateUser({ phone: phoneForm.phone, phone_confirmed: true }); toast({ title: "手机号绑定成功", status: "success", duration: 3000, isClosable: true, }); setPhoneForm({ phone: '', verificationCode: '' }); onPhoneClose(); } catch (error) { toast({ title: "绑定失败", description: error.message, status: "error", duration: 3000, isClosable: true, }); } finally { setIsLoading(false); } }; // 更换邮箱 const handleEmailBind = async () => { setIsLoading(true); try { // 调用真实的邮箱绑定API const res = await fetch(getApiBase() + '/api/account/email/bind', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ email: emailForm.email, code: emailForm.verificationCode }) }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || '绑定失败'); } // 更新用户信息 updateUser({ email: data.user.email, email_confirmed: data.user.email_confirmed }); // 🎯 追踪邮箱绑定成功 profileEvents.trackAccountBound('email', true); toast({ title: "邮箱绑定成功", status: "success", duration: 3000, isClosable: true, }); setEmailForm({ email: '', verificationCode: '' }); onEmailClose(); } catch (error) { // 🎯 追踪邮箱绑定失败 profileEvents.trackAccountBound('email', false); toast({ title: "绑定失败", description: error.message, status: "error", duration: 3000, isClosable: true, }); } finally { setIsLoading(false); } }; return ( {/* 页面标题 */} 账户设置 账户绑定 {/* 账户绑定 */} {/* 手机号绑定 */} 手机号绑定 {user?.phone || '未绑定手机号'} {user?.phone_confirmed && ( 已验证 )} 绑定手机号可用于登录和接收重要通知 {user?.phone ? ( ) : ( )} {/* 邮箱绑定 */} 邮箱设置 {user?.email} {user?.email_confirmed && ( 已验证 )} 邮箱用于登录和接收重要通知 {/* 微信绑定 */} 微信绑定 {user?.has_wechat ? '已绑定微信' : '未绑定微信'} {user?.has_wechat && ( 已绑定 )} 绑定微信可使用微信一键登录 {user?.has_wechat ? ( ) : ( )} {/* 绑定手机号模态框 */} 绑定手机号 手机号 setPhoneForm(prev => ({ ...prev, phone: e.target.value }))} placeholder="请输入11位手机号" /> 验证码 setPhoneForm(prev => ({ ...prev, verificationCode: value }))} > {/* 更换邮箱模态框 */} 更换邮箱 新邮箱 setEmailForm(prev => ({ ...prev, email: e.target.value }))} placeholder="请输入新邮箱地址" /> 验证码 setEmailForm(prev => ({ ...prev, verificationCode: value }))} > ); }