该文本描述了一个安全支付表单的HTML结构和样式,使用Bootstrap框架和FontAwesome图标。表单包括卡持有人姓名、卡号、过期日期和CVV字段,并实现了实时验证功能。JavaScript用于处理表单提交和验证逻辑,确保用户输入有效。整体设计注重用户体验和安全性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secure Payment</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary-blue: #2563eb;
--dark-gray: #334155;
--light-gray: #e2e8f0;
--error-red: #dc2626;
}
body {
background: #f8fafc;
min-height: 100vh;
font-family: 'Inter', system-ui, sans-serif;
}
.payment-card {
max-width: 520px;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0,0,0,0.08);
background: white;
border: 1px solid var(--light-gray);
}
.form-label {
font-size: 0.875rem;
font-weight: 500;
color: var(--dark-gray);
margin-bottom: 0.5rem;
}
.input-icon {
position: absolute;
left: 16px; /* Increased spacing */
top: 50%;
transform: translateY(-50%);
color: #64748b;
font-size: 1rem;
}
.form-control {
padding: 0.875rem 1rem 0.875rem 44px; /* Adjusted padding */
border-radius: 8px;
border: 1px solid var(--light-gray);
transition: all 0.2s ease;
}
.form-control:focus {
border-color: var(--primary-blue);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.error-message {
font-size: 0.75rem;
color: var(--error-red);
margin-top: 4px;
display: none;
}
.submit-btn {
background: var(--primary-blue);
color: white;
padding: 1rem 2rem;
font-weight: 500;
border-radius: 8px;
transition: all 0.2s ease;
position: relative;
}
.submit-btn:disabled {
background: #94a3b8;
cursor: not-allowed;
}
.loading-spinner {
display: none;
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.payment-brands img {
height: 28px;
opacity: 0.6;
transition: opacity 0.2s ease;
}
</style>
</head>
<body class="d-flex align-items-center">
<div class="container py-5">
<div class="payment-card mx-auto">
<div class="p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="mb-0 fw-semibold">Payment Details</h4>
<div class="payment-brands d-flex gap-2">
<img src="https://upload.wikimedia.org/wikipedia/commons/5/5e/Visa_Inc._logo.svg" alt="Visa">
<img src="https://upload.wikimedia.org/wikipedia/commons/2/2a/Mastercard-logo.svg" alt="Mastercard">
<img src="https://upload.wikimedia.org/wikipedia/commons/3/30/American_Express_logo.svg" alt="Amex">
</div>
</div>
<form id="paymentForm">
<!-- Cardholder Name -->
<div class="mb-4">
<label class="form-label">Cardholder Name</label>
<div class="position-relative">
<i class="fas fa-user input-icon"></i>
<input type="text"
class="form-control"
id="cardholderName"
placeholder="John Doe"
required>
<div class="error-message" id="nameError">Please enter valid name</div>
</div>
</div>
<!-- Card Number -->
<div class="mb-4">
<label class="form-label">Card Number</label>
<div class="position-relative">
<i class="fas fa-credit-card input-icon"></i>
<input type="text"
class="form-control"
id="cardNumber"
placeholder="4242 4242 4242 4242"
maxlength="19"
required>
<div class="error-message" id="cardError">Invalid card number</div>
</div>
</div>
<div class="row g-3 mb-4">
<!-- Expiration Date -->
<div class="col-md-6">
<label class="form-label">Expiration Date</label>
<div class="position-relative">
<i class="fas fa-calendar-alt input-icon"></i>
<input type="text"
class="form-control"
id="expiryDate"
placeholder="MM/YY"
maxlength="5"
required>
<div class="error-message" id="expiryError">MM/YY required</div>
</div>
</div>
<!-- CVV -->
<div class="col-md-6">
<label class="form-label">CVV</label>
<div class="position-relative">
<i class="fas fa-lock input-icon"></i>
<input type="text"
class="form-control"
id="cvv"
placeholder="123"
maxlength="3"
required>
<div class="error-message" id="cvvError">3 digits required</div>
</div>
</div>
</div>
<button type="submit" class="btn w-100 submit-btn" disabled>
<span class="submit-text">Pay $125.00</span>
<span class="loading-spinner"></span>
</button>
</form>
</div>
</div>
</div>
<script>
const form = document.getElementById('paymentForm');
const submitBtn = form.querySelector('button[type="submit"]');
let isSubmitting = false;
// 实时验证逻辑
function validateField(field, validateFn, errorElement) {
field.addEventListener('input', () => {
const isValid = validateFn(field.value);
errorElement.style.display = isValid ? 'none' : 'block';
field.classList.toggle('is-invalid', !isValid);
updateSubmitButton();
});
}
// 字段验证规则
const validators = {
name: value => value.trim().length >= 2,
card: value => /^\d{16}$/.test(value.replace(/ /g, '')),
expiry: value => /^(0[1-9]|1[0-2])\/?([0-9]{2})$/.test(value),
cvv: value => /^\d{3}$/.test(value)
};
// 初始化验证
validateField(document.getElementById('cardholderName'),
validators.name, document.getElementById('nameError'));
validateField(document.getElementById('cardNumber'), value => {
const cleaned = value.replace(/ /g, '');
return cleaned.length === 16 && /^\d+$/.test(cleaned);
}, document.getElementById('cardError'));
validateField(document.getElementById('expiryDate'),
validators.expiry, document.getElementById('expiryError'));
validateField(document.getElementById('cvv'),
validators.cvv, document.getElementById('cvvError'));
// 卡号格式化
document.getElementById('cardNumber').addEventListener('input', function(e) {
const value = e.target.value.replace(/ /g, '');
let formatted = value.match(/.{1,4}/g)?.join(' ') || '';
e.target.value = formatted.substring(0, 19);
});
// 更新提交按钮状态
function updateSubmitButton() {
const isValid = Object.values(validators).every((validator, index) => {
const field = form.elements[index];
return validator(field.value);
});
submitBtn.disabled = !isValid || isSubmitting;
}
// 表单提交处理
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (isSubmitting) return;
isSubmitting = true;
submitBtn.disabled = true;
submitBtn.querySelector('.submit-text').style.opacity = '0';
submitBtn.querySelector('.loading-spinner').style.display = 'block';
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1500));
alert('Payment successful!');
} catch (error) {
console.error(error);
alert('Payment failed. Please try again.');
} finally {
isSubmitting = false;
submitBtn.disabled = false;
submitBtn.querySelector('.submit-text').style.opacity = '1';
submitBtn.querySelector('.loading-spinner').style.display = 'none';
}
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>