|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React from 'react'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import { Modal, Button, Input, Typography } from '@douyinfe/semi-ui'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const TwoFactorAuthModal = ({ |
|
|
visible, |
|
|
code, |
|
|
loading, |
|
|
onCodeChange, |
|
|
onVerify, |
|
|
onCancel, |
|
|
title, |
|
|
description, |
|
|
placeholder, |
|
|
}) => { |
|
|
const { t } = useTranslation(); |
|
|
|
|
|
const handleKeyDown = (e) => { |
|
|
if (e.key === 'Enter' && code && !loading) { |
|
|
onVerify(); |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<Modal |
|
|
title={ |
|
|
<div className='flex items-center'> |
|
|
<div className='w-8 h-8 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center mr-3'> |
|
|
<svg |
|
|
className='w-4 h-4 text-blue-600 dark:text-blue-400' |
|
|
fill='currentColor' |
|
|
viewBox='0 0 20 20' |
|
|
> |
|
|
<path |
|
|
fillRule='evenodd' |
|
|
d='M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z' |
|
|
clipRule='evenodd' |
|
|
/> |
|
|
</svg> |
|
|
</div> |
|
|
{title || t('安全验证')} |
|
|
</div> |
|
|
} |
|
|
visible={visible} |
|
|
onCancel={onCancel} |
|
|
footer={ |
|
|
<> |
|
|
<Button onClick={onCancel}>{t('取消')}</Button> |
|
|
<Button |
|
|
type='primary' |
|
|
loading={loading} |
|
|
disabled={!code || loading} |
|
|
onClick={onVerify} |
|
|
> |
|
|
{t('验证')} |
|
|
</Button> |
|
|
</> |
|
|
} |
|
|
width={500} |
|
|
style={{ maxWidth: '90vw' }} |
|
|
> |
|
|
<div className='space-y-6'> |
|
|
{/* 安全提示 */} |
|
|
<div className='bg-blue-50 dark:bg-blue-900 rounded-lg p-4'> |
|
|
<div className='flex items-start'> |
|
|
<svg |
|
|
className='w-5 h-5 text-blue-600 dark:text-blue-400 mt-0.5 mr-3 flex-shrink-0' |
|
|
fill='currentColor' |
|
|
viewBox='0 0 20 20' |
|
|
> |
|
|
<path |
|
|
fillRule='evenodd' |
|
|
d='M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z' |
|
|
clipRule='evenodd' |
|
|
/> |
|
|
</svg> |
|
|
<div> |
|
|
<Typography.Text |
|
|
strong |
|
|
className='text-blue-800 dark:text-blue-200' |
|
|
> |
|
|
{t('安全验证')} |
|
|
</Typography.Text> |
|
|
<Typography.Text className='block text-blue-700 dark:text-blue-300 text-sm mt-1'> |
|
|
{description || t('为了保护账户安全,请验证您的两步验证码。')} |
|
|
</Typography.Text> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* 验证码输入 */} |
|
|
<div> |
|
|
<Typography.Text strong className='block mb-2'> |
|
|
{t('验证身份')} |
|
|
</Typography.Text> |
|
|
<Input |
|
|
placeholder={placeholder || t('请输入认证器验证码或备用码')} |
|
|
value={code} |
|
|
onChange={onCodeChange} |
|
|
size='large' |
|
|
maxLength={8} |
|
|
onKeyDown={handleKeyDown} |
|
|
autoFocus |
|
|
/> |
|
|
<Typography.Text type='tertiary' size='small' className='mt-2 block'> |
|
|
{t( |
|
|
'支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。', |
|
|
)} |
|
|
</Typography.Text> |
|
|
</div> |
|
|
</div> |
|
|
</Modal> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default TwoFactorAuthModal; |
|
|
|