| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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;
|
|
|