@model Dpz.Core.Authenticator.SetupCode
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<title>双因素绑定 - 认证中心</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="@Url.Content("~/css/global.css")" asp-append-version="true"/>
</head>
<body class="twofactor-page">
<div class="container">
<div class="header">
<h1>双因素身份验证</h1>
<p>请使用您的身份验证应用扫描二维码</p>
</div>
<form method="post" asp-action="HandleBindTwoFactor" autocomplete="off" data-submit-loading="true">
<input type="hidden" name="fromUrl" value="@TempData["FromUrl"]"/>
<div class="qr-section">
<div class="qr-wrapper">
<img class="qr-image" alt="双因素验证二维码" src="@Model.QrCodeSetupImageUrl"/>
</div>
</div>
<div class="manual-key-section">
<label class="manual-key-label">手动输入密钥</label>
<div class="manual-key-container">
<span class="manual-key">@Model.ManualEntryKey</span>
<button type="button" class="copy-button" onclick="copyToClipboard('@Model.ManualEntryKey')" title="复制密钥">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
</div>
</div>
<div class="input-section">
<label class="input-label" for="pinCode">验证码</label>
<div class="input-wrapper">
<input
id="pinCode"
name="pinCode"
type="text"
class="pin-input"
placeholder="请输入6位验证码"
pattern="[0-9]{6}"
maxlength="6"
minlength="6"
required
autocomplete="one-time-code"
inputmode="numeric"
/>
</div>
</div>
@if (TempData["Msg"] is string message)
{
<div class="error-message">
@message
</div>
}
<button type="submit" class="submit-button" data-loading-text="验证中...">
验证并绑定
</button>
<div class="skip-section">
<a href="@(TempData["FromUrl"] ?? "/")" class="skip-link" onclick="return confirmSkip()">
暂不绑定,返回原页面
</a>
</div>
</form>
<div class="footer">
<p>请确保您的设备时间准确,验证码每30秒更新一次</p>
</div>
</div>
<script src="@Url.Content("~/js/ui-components.js")" asp-append-version="true"></script>
<script>
// 复制到剪贴板功能
function copyToClipboard(text) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function() {
showToast('密钥已复制到剪贴板');
}).catch(function(err) {
fallbackCopyTextToClipboard(text);
});
} else {
fallbackCopyTextToClipboard(text);
}
}
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
showToast('密钥已复制到剪贴板');
} else {
showToast('复制失败,请手动复制');
}
} catch (err) {
showToast('复制失败,请手动复制');
}
document.body.removeChild(textArea);
}
// 简单的提示消息
function showToast(message) {
const toast = document.createElement('div');
toast.textContent = message;
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: var(--primary-color);
color: white;
padding: 12px 24px;
border-radius: 8px;
box-shadow: var(--shadow-lg);
z-index: 1000;
font-size: 14px;
font-weight: 500;
animation: slideIn 0.3s ease-out;
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.animation = 'slideOut 0.3s ease-in';
setTimeout(() => {
document.body.removeChild(toast);
}, 300);
}, 3000);
}
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@@keyframes slideOut {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0; }
}
`;
document.head.appendChild(style);
// 确认跳过绑定
function confirmSkip() {
return confirm('暂不绑定双因素验证将会降低您的账户安全性。\n\n您确定要跳过绑定并返回原页面吗?');
}
// 输入框格式化
document.addEventListener('DOMContentLoaded', function() {
const pinInput = document.getElementById('pinCode');
pinInput.addEventListener('input', function(e) {
// 只允许数字输入
this.value = this.value.replace(/[^0-9]/g, '');
// 自动聚焦到下一个输入或提交
if (this.value.length === 6) {
this.blur();
}
});
// 自动聚焦
pinInput.focus();
});
</script>
</body>
</html>