@model Dpz.Core.Auth.Models.ChangePasswordModel
@{
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="change-password-page">
<div class="container">
<div class="header">
<h1>修改密码</h1>
<p>为了您的账户安全,请设置一个强密码</p>
</div>
<form method="post" asp-action="HandleChangePassword" autocomplete="off" data-submit-loading="true">
@Html.AntiForgeryToken()
<!-- 当前密码 -->
<div class="input-section">
<label class="input-label" for="Password">当前密码</label>
<div class="input-wrapper">
<input
asp-for="Password"
type="password"
class="text-input"
placeholder="请输入当前密码"
autocomplete="current-password"
required
autofocus
/>
<button type="button" class="toggle-visibility" onclick="togglePasswordVisibility('Password')" aria-label="显示或隐藏密码">
<svg class="eye-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
</div>
<span asp-validation-for="Password" class="field-validation-error"></span>
</div>
<!-- 新密码 -->
<div class="input-section">
<label class="input-label" for="NewPassword">新密码</label>
<div class="input-wrapper">
<input
asp-for="NewPassword"
type="password"
class="text-input"
placeholder="请输入新密码"
autocomplete="new-password"
required
/>
<button type="button" class="toggle-visibility" onclick="togglePasswordVisibility('NewPassword')" aria-label="显示或隐藏密码">
<svg class="eye-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
</div>
<span asp-validation-for="NewPassword" class="field-validation-error"></span>
<!-- 密码强度指示器 -->
<div class="password-strength-container">
<div class="password-strength-label">密码强度:</div>
<div class="password-strength-indicator">
<div class="strength-bar" id="strengthBar"></div>
</div>
<div class="password-strength-text" id="strengthText">请输入密码</div>
</div>
<!-- 密码要求提示 -->
<div class="password-requirements">
<div class="requirement-item" id="req-length">
<span class="requirement-icon">○</span>
至少8个字符
</div>
<div class="requirement-item" id="req-ascii">
<span class="requirement-icon">○</span>
只包含ASCII可打印字符
</div>
<div class="requirement-item" id="req-no-space">
<span class="requirement-icon">○</span>
不包含空格
</div>
</div>
</div>
<!-- 确认新密码 -->
<div class="input-section">
<label class="input-label" for="NewRepeatPassword">确认新密码</label>
<div class="input-wrapper">
<input
asp-for="NewRepeatPassword"
type="password"
class="text-input"
placeholder="请再次输入新密码"
autocomplete="new-password"
required
/>
<button type="button" class="toggle-visibility" onclick="togglePasswordVisibility('NewRepeatPassword')" aria-label="显示或隐藏密码">
<svg class="eye-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
</div>
<span asp-validation-for="NewRepeatPassword" class="field-validation-error"></span>
<div class="password-match-indicator" id="passwordMatch"></div>
</div>
<!-- 双因素验证码(如已启用) -->
<div class="input-section">
<label class="input-label" for="PinCode">双因素验证码(如已启用)</label>
<div class="input-wrapper">
<input
asp-for="PinCode"
type="text"
class="pin-input"
placeholder="请输入6位验证码(可选)"
pattern="[0-9]{6}"
maxlength="6"
minlength="6"
autocomplete="one-time-code"
inputmode="numeric"
/>
</div>
<span asp-validation-for="PinCode" class="field-validation-error"></span>
<div class="input-hint">
如果您已启用双因素验证,请输入当前验证码
</div>
</div>
@if (TempData["Msg"] is string message)
{
<div class="error-message">
@message
</div>
}
@if (TempData["Message"] is string successMessage)
{
<div class="success-message">
@successMessage
</div>
}
<!-- 操作按钮 -->
<div class="action-buttons">
<button type="submit" class="submit-button" data-loading-text="修改中..." id="submitBtn" disabled>
确认修改密码
</button>
<a href="@Url.Action("Index", "Home")" class="cancel-button">
取消修改
</a>
</div>
</form>
<div class="footer">
<p>修改密码后,您需要重新登录所有设备</p>
</div>
</div>
<script src="@Url.Content("~/js/ui-components.js")" asp-append-version="true"></script>
<script>
// 密码可见性切换
function togglePasswordVisibility(fieldId) {
const field = document.getElementById(fieldId);
const button = field.nextElementSibling;
const icon = button.querySelector('.eye-icon');
if (field.type === 'password') {
field.type = 'text';
icon.innerHTML = `
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
`;
} else {
field.type = 'password';
icon.innerHTML = `
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
`;
}
}
// 密码强度检测
function checkPasswordStrength(password) {
let score = 0;
const requirements = {
length: password.length >= 8,
ascii: /^[\x21-\x7E]*$/.test(password) && password.length > 0,
'no-space': !/\s/.test(password)
};
// 更新要求指示器
Object.keys(requirements).forEach(req => {
const element = document.getElementById(`req-${req}`);
const icon = element.querySelector('.requirement-icon');
if (requirements[req]) {
element.classList.add('met');
icon.textContent = '✓';
score++;
} else {
element.classList.remove('met');
icon.textContent = '○';
}
});
// 计算额外的强度因子
let strengthScore = score;
if (password.length >= 8) {
// 基于密码复杂度的额外评分
if (/[a-z]/.test(password)) strengthScore += 0.5; // 小写字母
if (/[A-Z]/.test(password)) strengthScore += 0.5; // 大写字母
if (/\d/.test(password)) strengthScore += 0.5; // 数字
if (/[^\da-zA-Z]/.test(password)) strengthScore += 0.5; // 特殊字符
if (password.length >= 12) strengthScore += 0.5; // 长度奖励
}
// 更新强度指示器
const strengthBar = document.getElementById('strengthBar');
const strengthText = document.getElementById('strengthText');
let strengthLevel = '';
let strengthClass = '';
if (score < 3) {
strengthLevel = '不符合要求';
strengthClass = 'weak';
} else if (strengthScore <= 4) {
strengthLevel = '基本';
strengthClass = 'weak';
} else if (strengthScore <= 5) {
strengthLevel = '中等';
strengthClass = 'medium';
} else {
strengthLevel = '强';
strengthClass = 'strong';
}
strengthBar.className = `strength-bar ${strengthClass}`;
strengthBar.style.width = `${Math.min(strengthScore / 6 * 100, 100)}%`;
strengthText.textContent = strengthLevel;
// 只有满足所有基本要求才算有效
return score === 3;
}
// 检查密码匹配
function checkPasswordMatch() {
const newPassword = document.getElementById('NewPassword').value;
const confirmPassword = document.getElementById('NewRepeatPassword').value;
const matchIndicator = document.getElementById('passwordMatch');
if (confirmPassword === '') {
matchIndicator.textContent = '';
matchIndicator.className = 'password-match-indicator';
return false;
}
if (newPassword === confirmPassword) {
matchIndicator.textContent = '✓ 密码匹配';
matchIndicator.className = 'password-match-indicator match';
return true;
} else {
matchIndicator.textContent = '✗ 密码不匹配';
matchIndicator.className = 'password-match-indicator no-match';
return false;
}
}
// 验证表单并启用/禁用提交按钮
function validateForm() {
const currentPassword = document.getElementById('Password').value;
const newPassword = document.getElementById('NewPassword').value;
const confirmPassword = document.getElementById('NewRepeatPassword').value;
const submitBtn = document.getElementById('submitBtn');
const isPasswordStrong = checkPasswordStrength(newPassword);
const isPasswordMatch = checkPasswordMatch();
const hasCurrentPassword = currentPassword.length > 0;
const isValid = hasCurrentPassword && isPasswordStrong && isPasswordMatch;
submitBtn.disabled = !isValid;
}
// 事件监听器
document.addEventListener('DOMContentLoaded', function() {
const passwordField = document.getElementById('Password');
const newPasswordField = document.getElementById('NewPassword');
const confirmPasswordField = document.getElementById('NewRepeatPassword');
const pinCodeField = document.getElementById('PinCode');
// 密码输入事件
[passwordField, newPasswordField, confirmPasswordField].forEach(field => {
field.addEventListener('input', validateForm);
});
// PIN码格式化
pinCodeField.addEventListener('input', function(e) {
this.value = this.value.replace(/[^0-9]/g, '');
});
// 初始验证
validateForm();
});
</script>
</body>
</html>