| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import { API, showError, showSuccess } from '../../helpers'; |
| import { |
| Button, |
| Card, |
| Divider, |
| Form, |
| Input, |
| Typography, |
| } from '@douyinfe/semi-ui'; |
| import React, { useState } from 'react'; |
|
|
| const { Title, Text, Paragraph } = Typography; |
|
|
| const TwoFAVerification = ({ onSuccess, onBack, isModal = false }) => { |
| const [loading, setLoading] = useState(false); |
| const [useBackupCode, setUseBackupCode] = useState(false); |
| const [verificationCode, setVerificationCode] = useState(''); |
|
|
| const handleSubmit = async () => { |
| if (!verificationCode) { |
| showError('请输入验证码'); |
| return; |
| } |
| |
| if (useBackupCode && verificationCode.length !== 8) { |
| showError('备用码必须是8位'); |
| return; |
| } else if (!useBackupCode && !/^\d{6}$/.test(verificationCode)) { |
| showError('验证码必须是6位数字'); |
| return; |
| } |
|
|
| setLoading(true); |
| try { |
| const res = await API.post('/api/user/login/2fa', { |
| code: verificationCode, |
| }); |
|
|
| if (res.data.success) { |
| showSuccess('登录成功'); |
| |
| localStorage.setItem('user', JSON.stringify(res.data.data)); |
| if (onSuccess) { |
| onSuccess(res.data.data); |
| } |
| } else { |
| showError(res.data.message); |
| } |
| } catch (error) { |
| showError('验证失败,请重试'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
|
|
| const handleKeyPress = (e) => { |
| if (e.key === 'Enter') { |
| handleSubmit(); |
| } |
| }; |
|
|
| if (isModal) { |
| return ( |
| <div className='space-y-4'> |
| <Paragraph className='text-gray-600 dark:text-gray-300'> |
| 请输入认证器应用显示的验证码完成登录 |
| </Paragraph> |
| |
| <Form onSubmit={handleSubmit}> |
| <Form.Input |
| field='code' |
| label={useBackupCode ? '备用码' : '验证码'} |
| placeholder={useBackupCode ? '请输入8位备用码' : '请输入6位验证码'} |
| value={verificationCode} |
| onChange={setVerificationCode} |
| onKeyPress={handleKeyPress} |
| size='large' |
| style={{ marginBottom: 16 }} |
| autoFocus |
| /> |
| |
| <Button |
| htmlType='submit' |
| type='primary' |
| loading={loading} |
| block |
| size='large' |
| style={{ marginBottom: 16 }} |
| > |
| 验证并登录 |
| </Button> |
| </Form> |
| |
| <Divider /> |
| |
| <div style={{ textAlign: 'center' }}> |
| <Button |
| theme='borderless' |
| type='tertiary' |
| onClick={() => { |
| setUseBackupCode(!useBackupCode); |
| setVerificationCode(''); |
| }} |
| style={{ marginRight: 16, color: '#1890ff', padding: 0 }} |
| > |
| {useBackupCode ? '使用认证器验证码' : '使用备用码'} |
| </Button> |
| |
| {onBack && ( |
| <Button |
| theme='borderless' |
| type='tertiary' |
| onClick={onBack} |
| style={{ color: '#1890ff', padding: 0 }} |
| > |
| 返回登录 |
| </Button> |
| )} |
| </div> |
| |
| <div className='bg-gray-50 dark:bg-gray-800 rounded-lg p-3'> |
| <Text size='small' type='secondary'> |
| <strong>提示:</strong> |
| <br /> |
| • 验证码每30秒更新一次 |
| <br /> |
| • 如果无法获取验证码,请使用备用码 |
| <br />• 每个备用码只能使用一次 |
| </Text> |
| </div> |
| </div> |
| ); |
| } |
|
|
| return ( |
| <div |
| style={{ |
| display: 'flex', |
| justifyContent: 'center', |
| alignItems: 'center', |
| minHeight: '60vh', |
| }} |
| > |
| <Card style={{ width: 400, padding: 24 }}> |
| <div style={{ textAlign: 'center', marginBottom: 24 }}> |
| <Title heading={3}>两步验证</Title> |
| <Paragraph type='secondary'> |
| 请输入认证器应用显示的验证码完成登录 |
| </Paragraph> |
| </div> |
| |
| <Form onSubmit={handleSubmit}> |
| <Form.Input |
| field='code' |
| label={useBackupCode ? '备用码' : '验证码'} |
| placeholder={useBackupCode ? '请输入8位备用码' : '请输入6位验证码'} |
| value={verificationCode} |
| onChange={setVerificationCode} |
| onKeyPress={handleKeyPress} |
| size='large' |
| style={{ marginBottom: 16 }} |
| autoFocus |
| /> |
| |
| <Button |
| htmlType='submit' |
| type='primary' |
| loading={loading} |
| block |
| size='large' |
| style={{ marginBottom: 16 }} |
| > |
| 验证并登录 |
| </Button> |
| </Form> |
| |
| <Divider /> |
| |
| <div style={{ textAlign: 'center' }}> |
| <Button |
| theme='borderless' |
| type='tertiary' |
| onClick={() => { |
| setUseBackupCode(!useBackupCode); |
| setVerificationCode(''); |
| }} |
| style={{ marginRight: 16, color: '#1890ff', padding: 0 }} |
| > |
| {useBackupCode ? '使用认证器验证码' : '使用备用码'} |
| </Button> |
| |
| {onBack && ( |
| <Button |
| theme='borderless' |
| type='tertiary' |
| onClick={onBack} |
| style={{ color: '#1890ff', padding: 0 }} |
| > |
| 返回登录 |
| </Button> |
| )} |
| </div> |
| |
| <div |
| style={{ |
| marginTop: 24, |
| padding: 16, |
| background: '#f6f8fa', |
| borderRadius: 6, |
| }} |
| > |
| <Text size='small' type='secondary'> |
| <strong>提示:</strong> |
| <br /> |
| • 验证码每30秒更新一次 |
| <br /> |
| • 如果无法获取验证码,请使用备用码 |
| <br />• 每个备用码只能使用一次 |
| </Text> |
| </div> |
| </Card> |
| </div> |
| ); |
| }; |
|
|
| export default TwoFAVerification; |
|
|