diff --git a/web/src/components/settings/TwoFASetting.js b/web/src/components/settings/TwoFASetting.js index af0896ad..148284dd 100644 --- a/web/src/components/settings/TwoFASetting.js +++ b/web/src/components/settings/TwoFASetting.js @@ -17,7 +17,13 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { API, showError, showSuccess, showWarning } from '../../helpers'; -import { Banner, Button, Card, Checkbox, Divider, Form, Input, Modal, Tag, Typography } from '@douyinfe/semi-ui'; +import { Banner, Button, Card, Checkbox, Divider, Input, Modal, Tag, Typography, Steps, Space, Badge } from '@douyinfe/semi-ui'; +import { + IconShield, + IconAlertTriangle, + IconRefresh, + IconCopy +} from '@douyinfe/semi-icons'; import React, { useEffect, useState } from 'react'; import { QRCodeSVG } from 'qrcode.react'; @@ -43,6 +49,7 @@ const TwoFASetting = () => { const [verificationCode, setVerificationCode] = useState(''); const [backupCodes, setBackupCodes] = useState([]); const [confirmDisable, setConfirmDisable] = useState(false); + const [currentStep, setCurrentStep] = useState(0); // 获取2FA状态 const fetchStatus = async () => { @@ -68,6 +75,7 @@ const TwoFASetting = () => { if (res.data.success) { setSetupData(res.data.data); setSetupModalVisible(true); + setCurrentStep(0); } else { showError(res.data.message); } @@ -95,6 +103,7 @@ const TwoFASetting = () => { setEnableModalVisible(false); setSetupModalVisible(false); setVerificationCode(''); + setCurrentStep(0); fetchStatus(); } else { showError(res.data.message); @@ -166,75 +175,250 @@ const TwoFASetting = () => { } }; - const copyBackupCodes = () => { - const codesText = backupCodes.join('\n'); - navigator.clipboard.writeText(codesText).then(() => { - showSuccess('备用码已复制到剪贴板'); + // 通用复制函数 + const copyTextToClipboard = (text, successMessage = '已复制到剪贴板') => { + navigator.clipboard.writeText(text).then(() => { + showSuccess(successMessage); }).catch(() => { showError('复制失败,请手动复制'); }); }; - return ( -
+ const copyBackupCodes = () => { + const codesText = backupCodes.join('\n'); + copyTextToClipboard(codesText, '备用码已复制到剪贴板'); + }; + + // 备用码展示组件 + const BackupCodesDisplay = ({ codes, title, onCopy }) => { + return ( +
+
+ + {title} + +
+ +
+ {codes.map((code, index) => ( +
+
+ + {code} + + + #{(index + 1).toString().padStart(2, '0')} + +
+
+ ))} +
+ + + +
+
+ ); + }; + + // 渲染设置模态框footer + const renderSetupModalFooter = () => { + return ( + <> + {currentStep > 0 && ( + + )} + {currentStep < 2 ? ( + + ) : ( + + )} + + ); + }; + + // 渲染禁用模态框footer + const renderDisableModalFooter = () => { + return ( + <> + + + + ); + }; + + // 渲染重新生成模态框footer + const renderRegenerateModalFooter = () => { + if (backupCodes.length > 0) { + return ( + + ); + } + + return ( + <> + + + + ); + }; + + return ( + <> + -
-
-
- - - +
+
+
+
-
-
两步验证设置
-
- 两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。 -
-
- 当前状态: +
+
+ + 两步验证设置 + {status.enabled ? ( - 已启用 + 已启用 ) : ( - 未启用 + 未启用 )} {status.locked && ( - 账户已锁定 + 账户已锁定 )}
+ + 两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。 + {status.enabled && ( -
+
剩余备用码:{status.backup_codes_remaining || 0} 个
)}
-
+
{!status.enabled ? ( ) : (
@@ -248,11 +432,7 @@ const TwoFASetting = () => { -
- - - -
+ 设置两步验证
} @@ -260,101 +440,66 @@ const TwoFASetting = () => { onCancel={() => { setSetupModalVisible(false); setSetupData(null); + setCurrentStep(0); + setVerificationCode(''); }} - footer={null} + footer={renderSetupModalFooter()} width={650} style={{ maxWidth: '90vw' }} > {setupData && (
- {/* 步骤 1:扫描二维码 */} -
-
-
- 1 -
- 扫描二维码 -
- - 使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码: - -
-
- -
-
-
- - 或手动输入密钥:{setupData.secret} - -
-
+ {/* 步骤进度 */} + + + + + - {/* 步骤 2:保存备用码 */} -
-
-
- 2 -
- 保存备用码 -
- - 请将以下备用码保存在安全的地方。如果丢失手机,可以使用这些备用码登录: - -
-
- {setupData.backup_codes.map((code, index) => ( -
- {code} + {/* 步骤内容 */} +
+ {currentStep === 0 && ( +
+ + 使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码: + +
+
+
- ))} +
+
+ + 或手动输入密钥:{setupData.secret} + +
- -
-
+ )} - {/* 步骤 3:验证设置 */} -
-
-
- 3 + {currentStep === 1 && ( +
+ {/* 备用码展示 */} + { + const codesText = setupData.backup_codes.join('\n'); + copyTextToClipboard(codesText, '备用码已复制到剪贴板'); + }} + />
- 验证设置 -
- - 输入认证器应用显示的6位数字验证码: - -
- - - + )}
)} @@ -364,11 +509,7 @@ const TwoFASetting = () => { -
- - - -
+ 禁用两步验证
} @@ -378,58 +519,73 @@ const TwoFASetting = () => { setVerificationCode(''); setConfirmDisable(false); }} - footer={null} + footer={renderDisableModalFooter()} width={550} + style={{ maxWidth: '90vw' }} > -
- -
警告:禁用两步验证将会:
-
    -
  • 降低您账户的安全性
  • -
  • 永久删除您的两步验证设置
  • -
  • 永久删除所有备用码(包括未使用的)
  • -
  • 需要重新完整设置才能再次启用
  • -
-
- 此操作不可撤销,请谨慎操作! -
-
- } - className="rounded-lg" - /> -
- + {/* 警告提示 */} +
+ -
- setConfirmDisable(e.target.checked)} - className="text-sm" - > - 我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销 - +
+ + {/* 内容区域 */} +
+
+ + 禁用后的影响: + +
    +
  • + + 降低您账户的安全性 +
  • +
  • + + 需要重新完整设置才能再次启用 +
  • +
  • + + 永久删除您的两步验证设置 +
  • +
  • + + 永久删除所有备用码(包括未使用的) +
  • +
- - + + + +
+
+ + 验证身份 + + +
+ +
+ setConfirmDisable(e.target.checked)} + className="text-sm" + > + 我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销 + +
+
+
@@ -437,11 +593,7 @@ const TwoFASetting = () => { -
- - - -
+ 重新生成备用码
} @@ -451,73 +603,64 @@ const TwoFASetting = () => { setVerificationCode(''); setBackupCodes([]); }} - footer={null} + footer={renderRegenerateModalFooter()} width={500} + style={{ maxWidth: '90vw' }} > -
+
{backupCodes.length === 0 ? ( <> - -
- + - - +
+ + {/* 验证区域 */} +
+
+ + 验证身份 + + +
+
) : ( <> -
-
- - - + {/* 成功提示 */} + +
+ + + 新的备用码已生成 +
- 新的备用码已生成 - - 请将以下备用码保存在安全的地方: - -
-
-
- {backupCodes.map((code, index) => ( -
- {code} -
- ))} -
- -
+ + 旧的备用码已失效,请保存新的备用码 + + + {/* 备用码展示 */} + + )}
-
+ ); };