Validator Server
<section class="p-6 bg-white rounded-lg shadow-sm">
<div class="max-w-md mx-auto space-y-6">
<h3 class="text-lg font-medium text-gray-900">Server-side Validation Form</h3>
<p class="text-sm text-gray-600">This form demonstrates server-side validation with appropriate feedback.</p>
<form id="server-validation-form" class="space-y-6">
<!-- 表单状态消息 -->
<div class="status-message hidden"></div>
<!-- 用户名输入 -->
<div class="form-control">
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
<div class="relative">
<input type="text" id="username" name="username" required
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
<div class="server-error text-xs text-red-500 mt-1 hidden"></div>
</div>
</div>
<!-- 电子邮件输入 -->
<div class="form-control">
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email Address</label>
<div class="relative">
<input type="email" id="email" name="email" required
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
<div class="server-error text-xs text-red-500 mt-1 hidden"></div>
</div>
</div>
<!-- 密码输入 -->
<div class="form-control">
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
<div class="relative">
<input type="password" id="password" name="password" required
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
<div class="server-error text-xs text-red-500 mt-1 hidden"></div>
</div>
</div>
<!-- 提交按钮 -->
<div>
<button type="submit" class="w-full flex justify-center items-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<span id="submit-text">Create Account</span>
<div id="submit-spinner" class="hidden ml-2">
<svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</button>
</div>
</form>
<!-- 成功状态展示 -->
<div id="success-state" class="hidden">
<div class="text-center p-5 border border-green-200 bg-green-50 rounded-md">
<div class="inline-flex h-12 w-12 rounded-full bg-green-100 items-center justify-center mb-4">
<svg class="h-6 w-6 text-green-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 class="text-lg font-medium text-green-800">Account Created Successfully</h3>
<p class="text-sm text-green-600 mt-2">Your account has been created and you are now logged in.</p>
<button id="reset-demo" class="mt-4 px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
Reset Demo
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('server-validation-form');
const submitButton = form.querySelector('button[type="submit"]');
const submitText = document.getElementById('submit-text');
const submitSpinner = document.getElementById('submit-spinner');
const statusMessage = document.querySelector('.status-message');
const successState = document.getElementById('success-state');
const resetButton = document.getElementById('reset-demo');
// 表单提交处理
form.addEventListener('submit', function(e) {
e.preventDefault();
// 重置所有错误状态
resetFormErrors();
// 禁用按钮并显示加载状态
submitButton.disabled = true;
submitText.textContent = 'Creating Account...';
submitSpinner.classList.remove('hidden');
// 模拟向服务器发送请求
setTimeout(() => {
// 获取表单数据
const formData = {
username: document.getElementById('username').value,
email: document.getElementById('email').value,
password: document.getElementById('password').value
};
// 模拟服务器验证
const errors = simulateServerValidation(formData);
if (Object.keys(errors).length > 0) {
// 显示错误
displayServerErrors(errors);
// 重置按钮状态
submitButton.disabled = false;
submitText.textContent = 'Create Account';
submitSpinner.classList.add('hidden');
// 显示全局错误消息
statusMessage.textContent = 'Please correct the errors and try again.';
statusMessage.classList.remove('hidden', 'bg-green-50', 'text-green-800', 'border-green-200');
statusMessage.classList.add('p-3', 'rounded-md', 'text-sm', 'bg-red-50', 'text-red-800', 'border', 'border-red-200');
} else {
// 模拟成功创建账户
setTimeout(() => {
// 隐藏表单,显示成功状态
form.classList.add('hidden');
successState.classList.remove('hidden');
// 隐藏状态消息
statusMessage.classList.add('hidden');
}, 1000);
}
}, 2000);
});
// 重置演示
resetButton.addEventListener('click', function() {
// 显示表单,隐藏成功状态
form.classList.remove('hidden');
successState.classList.add('hidden');
// 清空表单
form.reset();
// 启用按钮并重置文本
submitButton.disabled = false;
submitText.textContent = 'Create Account';
submitSpinner.classList.add('hidden');
// 重置所有错误状态
resetFormErrors();
statusMessage.classList.add('hidden');
});
// 模拟服务器验证
function simulateServerValidation(data) {
const errors = {};
// 用户名验证
if (data.username === 'admin') {
errors.username = 'Username "admin" is reserved';
} else if (data.username.length < 3) {
errors.username = 'Username must be at least 3 characters';
}
// 电子邮件验证
if (data.email === '[email protected]') {
errors.email = 'This email is already registered';
} else if (!data.email.includes('@')) {
errors.email = 'Please enter a valid email address';
}
// 密码验证
if (data.password === 'password' || data.password === '123456') {
errors.password = 'Password is too common, please choose a stronger password';
} else if (data.password.length < 8) {
errors.password = 'Password must be at least 8 characters long';
}
return errors;
}
// 显示服务器错误
function displayServerErrors(errors) {
for (const field in errors) {
if (errors.hasOwnProperty(field)) {
const input = document.getElementById(field);
const errorElement = input.parentElement.querySelector('.server-error');
// 显示错误消息
errorElement.textContent = errors[field];
errorElement.classList.remove('hidden');
// 添加错误样式
input.classList.add('border-red-500');
input.classList.remove('border-gray-300');
// 添加焦点事件以清除错误
input.addEventListener('input', function onInput() {
input.classList.remove('border-red-500');
input.classList.add('border-gray-300');
errorElement.classList.add('hidden');
// 移除事件监听器
input.removeEventListener('input', onInput);
});
}
}
}
// 重置所有表单错误
function resetFormErrors() {
// 重置所有服务器错误消息
const serverErrors = document.querySelectorAll('.server-error');
serverErrors.forEach(error => {
error.classList.add('hidden');
error.textContent = '';
});
// 重置所有输入框样式
const inputs = form.querySelectorAll('input');
inputs.forEach(input => {
input.classList.remove('border-red-500');
input.classList.add('border-gray-300');
});
}
// 设置测试数据按钮
const usernameInput = document.getElementById('username');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
// 添加测试数据注释
const usernameComment = document.createElement('p');
usernameComment.classList.add('text-xs', 'text-gray-500', 'mt-1');
usernameComment.textContent = 'Try "admin" to see an error';
usernameInput.parentElement.appendChild(usernameComment);
const emailComment = document.createElement('p');
emailComment.classList.add('text-xs', 'text-gray-500', 'mt-1');
emailComment.textContent = 'Try "[email protected]" to see an error';
emailInput.parentElement.appendChild(emailComment);
const passwordComment = document.createElement('p');
passwordComment.classList.add('text-xs', 'text-gray-500', 'mt-1');
passwordComment.textContent = 'Try "password" to see an error';
passwordInput.parentElement.appendChild(passwordComment);
});
</script>
</section>
Copied to clipboard