238 lines
9.4 KiB
HTML
238 lines
9.4 KiB
HTML
{% extends "layout/base.html" %}
|
||
|
||
{% block title %}修改密码 - CHM考勤管理系统{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid py-4">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<!-- 页面标题 -->
|
||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||
<h2><i class="fas fa-key me-2"></i>修改密码</h2>
|
||
<nav aria-label="breadcrumb">
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item">
|
||
<a href="{{ url_for('admin.dashboard' if current_user.is_admin() else 'student.dashboard') }}">
|
||
{% if current_user.is_admin() %}控制台{% else %}首页{% endif %}
|
||
</a>
|
||
</li>
|
||
<li class="breadcrumb-item"><a href="{{ url_for('auth.profile') }}">个人信息</a></li>
|
||
<li class="breadcrumb-item active">修改密码</li>
|
||
</ol>
|
||
</nav>
|
||
</div>
|
||
|
||
<!-- 修改密码表单 -->
|
||
<div class="row">
|
||
<div class="col-lg-6 mx-auto">
|
||
<div class="card shadow">
|
||
<div class="card-header bg-warning text-dark">
|
||
<h5 class="mb-0">
|
||
<i class="fas fa-shield-alt me-2"></i>安全设置
|
||
</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<!-- 安全提示 -->
|
||
<div class="alert alert-info">
|
||
<i class="fas fa-info-circle me-2"></i>
|
||
<strong>密码要求:</strong>
|
||
<ul class="mb-0 mt-2">
|
||
<li>长度至少6位</li>
|
||
<li>必须包含字母和数字</li>
|
||
<li>建议使用字母、数字和特殊字符的组合</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<form method="POST" id="changePasswordForm">
|
||
<!-- 当前密码 -->
|
||
<div class="mb-3">
|
||
<label for="current_password" class="form-label">
|
||
<i class="fas fa-lock me-1"></i>当前密码 <span class="text-danger">*</span>
|
||
</label>
|
||
<div class="input-group">
|
||
<input type="password" class="form-control" id="current_password" name="current_password" required>
|
||
<button class="btn btn-outline-secondary" type="button" onclick="togglePassword('current_password')">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新密码 -->
|
||
<div class="mb-3">
|
||
<label for="new_password" class="form-label">
|
||
<i class="fas fa-key me-1"></i>新密码 <span class="text-danger">*</span>
|
||
</label>
|
||
<div class="input-group">
|
||
<input type="password" class="form-control" id="new_password" name="new_password" required>
|
||
<button class="btn btn-outline-secondary" type="button" onclick="togglePassword('new_password')">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
<div class="form-text">
|
||
<div id="password-strength" class="mt-2"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 确认密码 -->
|
||
<div class="mb-4">
|
||
<label for="confirm_password" class="form-label">
|
||
<i class="fas fa-check-double me-1"></i>确认新密码 <span class="text-danger">*</span>
|
||
</label>
|
||
<div class="input-group">
|
||
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
|
||
<button class="btn btn-outline-secondary" type="button" onclick="togglePassword('confirm_password')">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
<div id="password-match" class="form-text"></div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="d-grid gap-2 d-md-flex justify-content-md-center">
|
||
<button type="submit" class="btn btn-primary btn-lg me-md-2">
|
||
<i class="fas fa-save me-1"></i>保存密码
|
||
</button>
|
||
<a href="{{ url_for('auth.profile') }}" class="btn btn-secondary btn-lg">
|
||
<i class="fas fa-times me-1"></i>取消
|
||
</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.card {
|
||
border: none;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.card-header {
|
||
border-radius: 10px 10px 0 0 !important;
|
||
}
|
||
|
||
.input-group .btn {
|
||
border-left: none;
|
||
}
|
||
|
||
.password-strength-weak { color: #dc3545; }
|
||
.password-strength-medium { color: #ffc107; }
|
||
.password-strength-strong { color: #28a745; }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
<script>
|
||
// 切换密码显示/隐藏
|
||
function togglePassword(fieldId) {
|
||
const field = document.getElementById(fieldId);
|
||
const button = field.nextElementSibling;
|
||
const icon = button.querySelector('i');
|
||
|
||
if (field.type === 'password') {
|
||
field.type = 'text';
|
||
icon.className = 'fas fa-eye-slash';
|
||
} else {
|
||
field.type = 'password';
|
||
icon.className = 'fas fa-eye';
|
||
}
|
||
}
|
||
|
||
// 密码强度检查
|
||
document.getElementById('new_password').addEventListener('input', function() {
|
||
const password = this.value;
|
||
const strengthDiv = document.getElementById('password-strength');
|
||
|
||
if (password.length === 0) {
|
||
strengthDiv.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
let score = 0;
|
||
let feedback = [];
|
||
|
||
// 长度检查
|
||
if (password.length >= 6) score += 1;
|
||
else feedback.push('至少6位字符');
|
||
|
||
// 包含字母
|
||
if (/[a-zA-Z]/.test(password)) score += 1;
|
||
else feedback.push('包含字母');
|
||
|
||
// 包含数字
|
||
if (/\d/.test(password)) score += 1;
|
||
else feedback.push('包含数字');
|
||
|
||
// 包含特殊字符
|
||
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) score += 1;
|
||
|
||
let strengthText = '';
|
||
let strengthClass = '';
|
||
|
||
if (score < 2) {
|
||
strengthText = '弱';
|
||
strengthClass = 'password-strength-weak';
|
||
} else if (score < 3) {
|
||
strengthText = '中等';
|
||
strengthClass = 'password-strength-medium';
|
||
} else {
|
||
strengthText = '强';
|
||
strengthClass = 'password-strength-strong';
|
||
}
|
||
|
||
strengthDiv.innerHTML = `<span class="${strengthClass}">密码强度: ${strengthText}</span>`;
|
||
if (feedback.length > 0) {
|
||
strengthDiv.innerHTML += `<br><small class="text-muted">建议: ${feedback.join(', ')}</small>`;
|
||
}
|
||
});
|
||
|
||
// 密码匹配检查
|
||
document.getElementById('confirm_password').addEventListener('input', function() {
|
||
const newPassword = document.getElementById('new_password').value;
|
||
const confirmPassword = this.value;
|
||
const matchDiv = document.getElementById('password-match');
|
||
|
||
if (confirmPassword.length === 0) {
|
||
matchDiv.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
if (newPassword === confirmPassword) {
|
||
matchDiv.innerHTML = '<span class="text-success"><i class="fas fa-check me-1"></i>密码匹配</span>';
|
||
} else {
|
||
matchDiv.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>密码不匹配</span>';
|
||
}
|
||
});
|
||
|
||
// 表单提交验证
|
||
document.getElementById('changePasswordForm').addEventListener('submit', function(e) {
|
||
const newPassword = document.getElementById('new_password').value;
|
||
const confirmPassword = document.getElementById('confirm_password').value;
|
||
|
||
if (newPassword !== confirmPassword) {
|
||
e.preventDefault();
|
||
alert('新密码与确认密码不匹配!');
|
||
return false;
|
||
}
|
||
|
||
if (newPassword.length < 6) {
|
||
e.preventDefault();
|
||
alert('新密码长度至少6位!');
|
||
return false;
|
||
}
|
||
|
||
if (!/^(?=.*[a-zA-Z])(?=.*\d).+$/.test(newPassword)) {
|
||
e.preventDefault();
|
||
alert('新密码必须包含字母和数字!');
|
||
return false;
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %}
|