feat: 10.10线上最新代码提交
This commit is contained in:
@@ -77,6 +77,14 @@ export default function SettingsPage() {
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
// 密码状态 - 默认假设是普通用户,获取到数据后再更新
|
||||
const [passwordStatus, setPasswordStatus] = useState({
|
||||
isWechatUser: false,
|
||||
hasPassword: true,
|
||||
needsFirstTimeSetup: false
|
||||
});
|
||||
const [passwordStatusLoading, setPasswordStatusLoading] = useState(true);
|
||||
const [phoneForm, setPhoneForm] = useState({
|
||||
phone: '',
|
||||
verificationCode: ''
|
||||
@@ -106,8 +114,53 @@ export default function SettingsPage() {
|
||||
allow_friend_requests: true
|
||||
});
|
||||
|
||||
// 获取密码状态
|
||||
const fetchPasswordStatus = async () => {
|
||||
try {
|
||||
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
||||
? ""
|
||||
: process.env.REACT_APP_API_URL || "http://49.232.185.254:5000";
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/api/account/password-status`, {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
console.log('密码状态数据:', data.data); // 调试信息
|
||||
setPasswordStatus(data.data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取密码状态失败:', error);
|
||||
} finally {
|
||||
setPasswordStatusLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 组件加载时获取密码状态
|
||||
React.useEffect(() => {
|
||||
fetchPasswordStatus();
|
||||
}, []);
|
||||
|
||||
// 修改密码
|
||||
const handlePasswordChange = async () => {
|
||||
const isFirstTimeSetup = passwordStatus.needsFirstTimeSetup;
|
||||
|
||||
// 如果不是首次设置且未提供当前密码
|
||||
if (!isFirstTimeSetup && !passwordForm.currentPassword) {
|
||||
toast({
|
||||
title: "请输入当前密码",
|
||||
description: "修改密码需要验证当前密码",
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
toast({
|
||||
title: "密码不匹配",
|
||||
@@ -132,24 +185,52 @@ export default function SettingsPage() {
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 这里应该调用后端API修改密码
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
toast({
|
||||
title: "密码修改成功",
|
||||
description: "请重新登录",
|
||||
status: "success",
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
// 调用后端API修改密码
|
||||
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
||||
? ""
|
||||
: process.env.REACT_APP_API_URL || "http://49.232.185.254:5000";
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/api/account/change-password`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include', // 包含认证信息
|
||||
body: JSON.stringify({
|
||||
currentPassword: passwordForm.currentPassword,
|
||||
newPassword: passwordForm.newPassword,
|
||||
isFirstSet: passwordStatus.needsFirstTimeSetup
|
||||
})
|
||||
});
|
||||
|
||||
setPasswordForm({ currentPassword: '', newPassword: '', confirmPassword: '' });
|
||||
onPasswordClose();
|
||||
const data = await response.json();
|
||||
|
||||
// 修改密码后需要重新登录
|
||||
setTimeout(() => {
|
||||
logout();
|
||||
}, 1000);
|
||||
if (response.ok && data.success) {
|
||||
const isFirstSet = passwordStatus.needsFirstTimeSetup;
|
||||
|
||||
toast({
|
||||
title: isFirstSet ? "密码设置成功" : "密码修改成功",
|
||||
description: isFirstSet ? "您现在可以使用手机号+密码登录了" : "请重新登录",
|
||||
status: "success",
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
|
||||
setPasswordForm({ currentPassword: '', newPassword: '', confirmPassword: '' });
|
||||
onPasswordClose();
|
||||
|
||||
// 刷新密码状态
|
||||
fetchPasswordStatus();
|
||||
|
||||
// 如果是修改密码(非首次设置),需要重新登录
|
||||
if (!isFirstSet) {
|
||||
setTimeout(() => {
|
||||
logout();
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
throw new Error(data.error || '密码修改失败');
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "修改失败",
|
||||
@@ -167,8 +248,26 @@ export default function SettingsPage() {
|
||||
const sendVerificationCode = async (type) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 这里应该调用后端API发送验证码
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
if (type === 'phone') {
|
||||
const res = await fetch((process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001') + '/api/account/phone/send-code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ phone: phoneForm.phone })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || '发送失败');
|
||||
} else {
|
||||
// 使用绑定邮箱的验证码API
|
||||
const res = await fetch((process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001') + '/api/account/email/send-bind-code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ email: emailForm.email })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || '发送失败');
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "验证码已发送",
|
||||
@@ -194,8 +293,14 @@ export default function SettingsPage() {
|
||||
const handlePhoneBind = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 这里应该调用后端API绑定手机号
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const res = await fetch((process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001') + '/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,
|
||||
@@ -228,16 +333,30 @@ export default function SettingsPage() {
|
||||
const handleEmailBind = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 这里应该调用后端API更换邮箱
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
// 调用真实的邮箱绑定API
|
||||
const res = await fetch((process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001') + '/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: emailForm.email,
|
||||
email_confirmed: true
|
||||
email: data.user.email,
|
||||
email_confirmed: data.user.email_confirmed
|
||||
});
|
||||
|
||||
toast({
|
||||
title: "邮箱更换成功",
|
||||
title: "邮箱绑定成功",
|
||||
status: "success",
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
@@ -247,7 +366,7 @@ export default function SettingsPage() {
|
||||
onEmailClose();
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "更换失败",
|
||||
title: "绑定失败",
|
||||
description: error.message,
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
@@ -373,13 +492,27 @@ export default function SettingsPage() {
|
||||
<CardBody>
|
||||
<HStack justify="space-between">
|
||||
<VStack align="start" spacing={1}>
|
||||
<Text fontWeight="medium">登录密码</Text>
|
||||
<Text fontSize="sm" color="gray.600">
|
||||
定期更换密码,保护账户安全
|
||||
<Text fontWeight="medium">
|
||||
{passwordStatus.needsFirstTimeSetup ? '设置登录密码' : '登录密码'}
|
||||
</Text>
|
||||
<Text fontSize="sm" color="gray.600">
|
||||
{passwordStatus.needsFirstTimeSetup
|
||||
? '您通过微信登录,建议设置密码以便其他方式登录'
|
||||
: '定期更换密码,保护账户安全'
|
||||
}
|
||||
</Text>
|
||||
{passwordStatus.isWechatUser && (
|
||||
<Text fontSize="xs" color="blue.500">
|
||||
微信用户
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
<Button leftIcon={<EditIcon />} onClick={onPasswordOpen}>
|
||||
修改密码
|
||||
<Button
|
||||
leftIcon={<EditIcon />}
|
||||
onClick={onPasswordOpen}
|
||||
isLoading={passwordStatusLoading}
|
||||
>
|
||||
{passwordStatus.needsFirstTimeSetup ? '设置密码' : '修改密码'}
|
||||
</Button>
|
||||
</HStack>
|
||||
</CardBody>
|
||||
@@ -411,7 +544,23 @@ export default function SettingsPage() {
|
||||
leftIcon={<DeleteIcon />}
|
||||
colorScheme="red"
|
||||
variant="outline"
|
||||
onClick={handlePhoneUnbind}
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await fetch((process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001') + '/api/account/phone/unbind', {
|
||||
method: 'POST',
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || '解绑失败');
|
||||
updateUser({ phone: null, phone_confirmed: false });
|
||||
toast({ title: '手机号解绑成功', status: 'success', duration: 3000, isClosable: true });
|
||||
} catch (e) {
|
||||
toast({ title: '解绑失败', description: e.message, status: 'error', duration: 3000, isClosable: true });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
解绑
|
||||
@@ -477,11 +626,64 @@ export default function SettingsPage() {
|
||||
leftIcon={<DeleteIcon />}
|
||||
colorScheme="red"
|
||||
variant="outline"
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await fetch((process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001') + '/api/account/wechat/unbind', {
|
||||
method: 'POST',
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || '解绑失败');
|
||||
updateUser({ has_wechat: false });
|
||||
toast({ title: '解绑成功', status: 'success', duration: 3000, isClosable: true });
|
||||
} catch (e) {
|
||||
toast({ title: '解绑失败', description: e.message, status: 'error', duration: 3000, isClosable: true });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
解绑微信
|
||||
</Button>
|
||||
) : (
|
||||
<Button leftIcon={<LinkIcon />} colorScheme="green">
|
||||
<Button leftIcon={<LinkIcon />} colorScheme="green" onClick={async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
|
||||
const res = await fetch(base + '/api/account/wechat/qrcode', { method: 'GET', credentials: 'include' });
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || '获取二维码失败');
|
||||
// 打开新的窗口进行扫码
|
||||
window.open(data.auth_url, '_blank');
|
||||
// 轮询绑定状态
|
||||
const sessionId = data.session_id;
|
||||
const start = Date.now();
|
||||
const poll = async () => {
|
||||
if (Date.now() - start > 300000) return; // 5分钟
|
||||
const r = await fetch(base + '/api/account/wechat/check', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ session_id: sessionId })
|
||||
});
|
||||
const j = await r.json();
|
||||
if (j.status === 'bind_ready') {
|
||||
updateUser({ has_wechat: true });
|
||||
toast({ title: '微信绑定成功', status: 'success', duration: 3000, isClosable: true });
|
||||
} else if (j.status === 'bind_conflict' || j.status === 'bind_failed' || j.status === 'expired') {
|
||||
toast({ title: '绑定未完成', description: j.status, status: 'error', duration: 3000, isClosable: true });
|
||||
} else {
|
||||
setTimeout(poll, 2000);
|
||||
}
|
||||
};
|
||||
setTimeout(poll, 1500);
|
||||
} catch (e) {
|
||||
toast({ title: '获取二维码失败', description: e.message, status: 'error', duration: 3000, isClosable: true });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}>
|
||||
绑定微信
|
||||
</Button>
|
||||
)}
|
||||
@@ -857,30 +1059,49 @@ export default function SettingsPage() {
|
||||
<Modal isOpen={isPasswordOpen} onClose={onPasswordClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>修改密码</ModalHeader>
|
||||
<ModalHeader>
|
||||
{passwordStatus.needsFirstTimeSetup ? '设置登录密码' : '修改密码'}
|
||||
</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<VStack spacing={4}>
|
||||
<FormControl>
|
||||
<FormLabel>当前密码</FormLabel>
|
||||
<InputGroup>
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={passwordForm.currentPassword}
|
||||
onChange={(e) => setPasswordForm(prev => ({
|
||||
...prev,
|
||||
currentPassword: e.target.value
|
||||
}))}
|
||||
/>
|
||||
<InputRightElement>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
icon={showPassword ? <ViewOffIcon /> : <ViewIcon />}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
{/* 微信用户说明 */}
|
||||
{passwordStatus.isWechatUser && passwordStatus.needsFirstTimeSetup && (
|
||||
<Alert status="info" borderRadius="md">
|
||||
<AlertIcon />
|
||||
<Box>
|
||||
<AlertTitle>设置密码以便多种方式登录</AlertTitle>
|
||||
<AlertDescription fontSize="sm">
|
||||
您当前通过微信登录,设置密码后可以使用手机号+密码的方式登录。
|
||||
</AlertDescription>
|
||||
</Box>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* 当前密码 - 仅非首次设置且非加载状态时显示 */}
|
||||
{!passwordStatusLoading && !passwordStatus.needsFirstTimeSetup && (
|
||||
<FormControl>
|
||||
<FormLabel>当前密码</FormLabel>
|
||||
<InputGroup>
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={passwordForm.currentPassword}
|
||||
onChange={(e) => setPasswordForm(prev => ({
|
||||
...prev,
|
||||
currentPassword: e.target.value
|
||||
}))}
|
||||
placeholder="请输入当前密码"
|
||||
/>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
<InputRightElement>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
icon={showPassword ? <ViewOffIcon /> : <ViewIcon />}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
/>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>新密码</FormLabel>
|
||||
@@ -916,7 +1137,7 @@ export default function SettingsPage() {
|
||||
onClick={handlePasswordChange}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
确认修改
|
||||
{passwordStatus.needsFirstTimeSetup ? '设置密码' : '确认修改'}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
||||
Reference in New Issue
Block a user