Files
yixiaogao/frontend/user-center.html

1753 lines
69 KiB
HTML
Raw Normal View History

2025-11-27 18:32:24 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户中心 - 易搜高</title>
<meta name="keywords" content="用户中心,个人信息,密码修改,易搜高">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<style>
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif;
background: #f5f6fa;
color: #333;
line-height: 1.6;
}
/* 头部样式 */
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
font-size: 24px;
font-weight: bold;
text-decoration: none;
color: white;
}
.nav-menu {
display: flex;
gap: 30px;
align-items: center;
}
.nav-menu a {
color: white;
text-decoration: none;
transition: opacity 0.3s;
font-weight: 500;
}
.nav-menu a:hover {
opacity: 0.8;
}
.nav-menu a.active {
opacity: 1;
border-bottom: 2px solid white;
padding-bottom: 3px;
}
/* 用户信息和登出按钮样式 */
.user-info {
display: flex;
align-items: center;
gap: 15px;
}
.user-name {
font-weight: 500;
}
.logout-btn {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 6px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.logout-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-1px);
}
/* 主内容区域 */
.main-content {
max-width: 1200px;
margin: 30px auto;
padding: 0 20px;
}
.page-title {
font-size: 28px;
color: #2c3e50;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 2px solid #e0e6ed;
}
/* 用户中心容器 */
.user-center-container {
display: flex;
gap: 30px;
}
/* 侧边栏 */
.sidebar {
width: 280px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
padding: 25px;
height: fit-content;
position: sticky;
top: 100px;
}
.user-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0 auto 20px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 32px;
font-weight: bold;
}
.sidebar-user-name {
text-align: center;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 5px;
}
.sidebar-join-date {
text-align: center;
font-size: 14px;
color: #95a5a6;
margin-bottom: 25px;
}
.sidebar-menu {
list-style: none;
}
.sidebar-menu li {
margin-bottom: 5px;
}
.sidebar-menu a {
display: block;
padding: 12px 16px;
color: #5a6c7d;
text-decoration: none;
border-radius: 8px;
transition: all 0.3s;
font-size: 15px;
}
.sidebar-menu a:hover {
background: #f5f7fa;
color: #667eea;
}
.sidebar-menu a.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
/* 内容区域 */
.content-area {
flex: 1;
background: white;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
padding: 30px;
}
.section-title {
font-size: 20px;
color: #2c3e50;
margin-bottom: 25px;
display: flex;
align-items: center;
gap: 10px;
}
.section-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background: #667eea;
color: white;
border-radius: 50%;
font-size: 14px;
}
/* 信息卡片 */
.info-card {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 25px;
}
.info-row {
display: flex;
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
width: 120px;
color: #6c757d;
font-weight: 500;
}
.info-value {
flex: 1;
color: #2c3e50;
font-weight: 600;
}
/* 表单样式 */
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
}
.form-input {
width: 100%;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 15px;
transition: border-color 0.3s;
}
.form-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.btn-secondary {
background: #6c757d;
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
margin-left: 10px;
}
.btn-secondary:hover {
background: #5a6268;
transform: translateY(-1px);
}
/* 消息提示 */
.message {
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
display: none;
}
.message-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.message-info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
/* 响应式设计 */
@media (max-width: 768px) {
.user-center-container {
flex-direction: column;
}
.sidebar {
width: 100%;
position: static;
margin-bottom: 30px;
}
.sidebar-menu {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.sidebar-menu li {
flex: 1;
min-width: calc(50% - 5px);
}
.info-row {
flex-direction: column;
}
.info-label {
width: 100%;
margin-bottom: 5px;
}
.btn-secondary {
margin-left: 0;
margin-top: 10px;
width: 100%;
}
}
@media (max-width: 480px) {
.header-content {
flex-direction: column;
gap: 15px;
}
.nav-menu {
flex-wrap: wrap;
justify-content: center;
gap: 15px;
}
.sidebar-menu li {
min-width: 100%;
}
}
</style>
</head>
<body>
<!-- 头部导航 -->
<header class="header">
<div class="header-content">
<a href="frontend.html" class="logo">易搜高</a>
<nav class="nav-menu">
<a href="frontend.html">首页监控</a>
<a href="history-articles.html">历史文章</a>
<a href="user-center.html" class="active">用户中心</a>
<!-- 用户信息和登出按钮会通过JavaScript动态添加 -->
<div id="userMenu"></div>
<a href="login.html" id="loginLink" class="login-link">登录/注册</a>
</nav>
</div>
</header>
<!-- 主内容区域 -->
<main class="main-content">
<h1 class="page-title">用户中心</h1>
<div class="user-center-container">
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="user-avatar" id="sidebar-avatar">U</div>
<h3 class="sidebar-user-name" id="sidebar-username">用户名</h3>
<p class="sidebar-join-date" id="sidebar-join-date">注册时间:--</p>
<!-- 侧边栏菜单更新 -->
<ul class="sidebar-menu">
<li><a href="#info-section" class="active" onclick="showSection('info-section')">个人信息</a></li>
<li><a href="#password-section" onclick="showSection('password-section')">修改密码</a></li>
<li><a href="#account-settings-section" onclick="showSection('account-settings-section')">账号设置</a></li>
</ul>
</aside>
<!-- 内容区域 -->
<div class="content-area">
<!-- 消息提示 -->
<div id="message" class="message"></div>
<!-- 个人信息区域 -->
<section id="info-section">
<h2 class="section-title">
<span class="section-icon">👤</span>
个人信息
</h2>
<div class="info-card">
<!-- 修复个人信息区域中重复的ID -->
<div class="info-row">
<span class="info-label">用户名</span>
<span class="info-value" id="info-username">--</span>
<span id="username-display" style="display: none;"></span>
</div>
<div class="info-row">
<span class="info-label">邮箱</span>
<span class="info-value" id="info-email">--</span>
</div>
<div class="info-row">
<span class="info-label">注册时间</span>
<span class="info-value" id="info-join-date">--</span>
</div>
<div class="info-row">
<span class="info-label">上次登录</span>
<span class="info-value" id="info-last-login">--</span>
</div>
<div class="info-row">
<span class="info-label">账号状态</span>
<span class="info-value" id="info-status">--</span>
</div>
</div>
</section>
<!-- 密码修改部分 -->
<section id="password-section" class="content-section" style="display: none;">
<h2 class="section-title">
<span class="section-icon">🔒</span>
修改密码
</h2>
<form id="password-form" onsubmit="return changePassword(event)">
<div class="form-group">
<label for="current-password" class="form-label">当前密码</label>
<input type="password" id="current-password" class="form-input" required placeholder="请输入当前密码">
</div>
<div class="form-group">
<label for="new-password" class="form-label">新密码</label>
<input type="password" id="new-password" class="form-input" required placeholder="请输入新密码"
oninput="checkPasswordStrength(this.value); checkPasswordMatch(this.value, document.getElementById('confirm-password').value)">
<!-- 密码强度指示器 -->
<div id="password-strength-container" class="password-strength-container">
<div class="password-strength-bar">
<div id="strength-bar" class="strength-bar"></div>
</div>
<div id="strength-text" class="strength-text">密码强度:未输入</div>
<div id="password-requirements" class="password-requirements">
<div class="requirement">✓ 至少8个字符</div>
<div class="requirement">✗ 包含小写字母</div>
<div class="requirement">✗ 包含大写字母</div>
<div class="requirement">✗ 包含数字</div>
<div class="requirement">✗ 包含特殊字符</div>
</div>
</div>
</div>
<div class="form-group">
<label for="confirm-password" class="form-label">确认新密码</label>
<input type="password" id="confirm-password" class="form-input" required placeholder="请再次输入新密码"
oninput="checkPasswordMatch(document.getElementById('new-password').value, this.value)">
<div id="password-match-message" class="password-match-message"></div>
</div>
<button type="submit" class="btn-primary">修改密码</button>
</form>
</section>
<!-- 在密码修改表单之后,账号设置区域 -->
<section id="account-settings-section" class="content-section" style="display: none;">
<h2 class="section-title">
<span class="section-icon">⚙️</span>
账号设置
</h2>
<div class="settings-form">
<div class="form-group">
<label for="notification-enabled" class="form-label">启用通知</label>
<div class="switch-container">
<input type="checkbox" id="notification-enabled" class="toggle-switch">
<label for="notification-enabled" class="switch-label"></label>
</div>
</div>
<div class="form-group">
<label for="theme-select" class="form-label">主题设置</label>
<select id="theme-select" class="form-input">
<option value="light">浅色主题</option>
<option value="dark">深色主题</option>
<option value="system">跟随系统</option>
</select>
</div>
<div class="form-group">
<label for="language-select" class="form-label">语言设置</label>
<select id="language-select" class="form-input">
<option value="zh-CN">简体中文</option>
<option value="en-US">English</option>
</select>
</div>
<div class="form-actions">
<button id="save-settings-btn" class="btn-primary">保存设置</button>
</div>
</div>
<!-- 账号注销区域 -->
<div class="account-delete-section">
<h3 class="section-title" style="font-size: 18px;">
<span class="section-icon">⚠️</span>
账号注销
</h3>
<div class="delete-warning">
<p><i class="warning-icon">⚠️</i> 账号注销后,所有数据将被永久删除,此操作无法撤销!</p>
</div>
<div class="form-group">
<label for="confirm-username" class="form-label">请输入您的用户名以确认注销:</label>
<input type="text" id="confirm-username" class="form-input" placeholder="输入用户名确认">
</div>
<div class="form-actions">
<button id="delete-account-btn" class="btn-danger">注销我的账号</button>
</div>
</div>
</section>
<!-- 账号注销确认对话框 -->
<div id="delete-confirm-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>确认账号注销</h3>
<button id="close-modal-btn" class="close-btn">×</button>
</div>
<div class="modal-body">
<p class="warning-text">⚠️ 您确定要永久删除您的账号吗?</p>
<p>此操作将:</p>
<ul>
<li>删除所有个人资料和设置</li>
<li>删除您的所有收藏和历史记录</li>
<li>无法恢复已删除的数据</li>
</ul>
<div class="form-group">
<label for="final-confirm">请输入 "DELETE" 以确认:</label>
<input type="text" id="final-confirm" class="form-control" placeholder="DELETE">
</div>
</div>
<div class="modal-footer">
<button id="cancel-delete-btn" class="btn btn-secondary">取消</button>
<button id="confirm-delete-btn" class="btn btn-danger">确认注销</button>
</div>
</div>
</div>
<!-- 账号设置相关 CSS 样式 -->
<style>
.switch-container {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.toggle-switch {
opacity: 0;
width: 0;
height: 0;
}
.switch-label {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.switch-label:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
.toggle-switch:checked + .switch-label {
background-color: #2196F3;
}
.toggle-switch:checked + .switch-label:before {
transform: translateX(26px);
}
.account-delete-section {
margin-top: 30px;
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
border: 1px solid #ddd;
}
.delete-warning {
padding: 10px;
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 4px;
margin-bottom: 20px;
}
.warning-icon {
font-size: 1.2em;
margin-right: 5px;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
width: 90%;
max-width: 500px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.modal-header h3 {
margin: 0;
color: #d9534f;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.modal-body {
margin-bottom: 20px;
}
.warning-text {
font-weight: bold;
color: #d9534f;
margin-bottom: 15px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.btn-danger {
background-color: #d9534f;
color: white;
border: none;
}
.btn-danger:hover {
background-color: #c9302c;
}
.btn-secondary {
background-color: #6c757d;
color: white;
border: none;
}
.btn-secondary:hover {
background-color: #5a6268;
border: none;
}
</style>
// 添加缺失的函数定义,确保功能完整性
function showMessage(message, type = 'info') {
const messageElement = document.getElementById('message');
if (messageElement) {
messageElement.textContent = message;
messageElement.className = `message message-${type}`;
messageElement.style.display = 'block';
// 3秒后自动隐藏消息
setTimeout(() => {
messageElement.style.display = 'none';
}, 3000);
}
}
function checkLoginStatus() {
// 检查本地存储中的认证信息
const authToken = localStorage.getItem('authToken') || localStorage.getItem('token') || sessionStorage.getItem('token');
const username = localStorage.getItem('username') || localStorage.getItem('nickname');
if (authToken && username) {
// 用户已登录更新UI
document.getElementById('loginLink').style.display = 'none';
const userMenu = document.getElementById('userMenu');
if (userMenu) {
userMenu.innerHTML = `
<div class="user-info">
<span class="user-name">${username}</span>
<button class="logout-btn" onclick="logout()">退出登录</button>
</div>
`;
}
// 加载用户信息
loadUserData();
} else {
// 用户未登录,跳转到登录页面
window.location.href = 'login.html';
}
}
function logout() {
// 清除本地存储的认证信息
localStorage.removeItem('authToken');
localStorage.removeItem('username');
localStorage.removeItem('userSettings');
localStorage.removeItem('lastLogin');
// 尝试调用登出API使用fetch而不是apiCall避免依赖本地函数
fetch('http://localhost:8000/api/user/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
}).finally(() => {
// 无论API调用成功与否都跳转到登录页面
window.location.href = 'login.html';
});
}
function clearAuthData() {
localStorage.removeItem('authToken');
localStorage.removeItem('username');
localStorage.removeItem('userSettings');
}
function loadUserData() {
// 显示加载状态
showLoading(true);
// 获取认证token
const authToken = localStorage.getItem('authToken') || localStorage.getItem('token') || sessionStorage.getItem('token');
// 调用后端API获取用户信息
fetch('http://localhost:8000/api/user/info', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
credentials: 'include'
})
.then(response => {
if (!response.ok) {
if (response.status === 401) {
// 认证失败,清除认证信息并重定向到登录页面
clearAuthData();
window.location.href = 'login.html';
throw new Error('认证失败,请重新登录');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data && data.data) {
// 使用后端返回的用户数据
const userData = data.data;
// 更新用户信息显示
updateUserInfoDisplay(userData);
}
hideLoading();
})
.catch(error => {
console.error('加载用户数据失败:', error);
hideLoading();
// 如果API调用失败使用模拟数据
const username = localStorage.getItem('username') || '测试用户';
const userData = {
username: username,
email: 'test@example.com',
nickname: username,
role: '普通用户',
joinDate: new Date().toLocaleDateString('zh-CN'),
lastLogin: new Date().toLocaleString('zh-CN')
};
updateUserInfoDisplay(userData);
});
}
// 增强版API调用包装函数
function apiCall(endpoint, method = 'GET', data = null, requiresAuth = true) {
const url = `http://localhost:8000/api${endpoint}`;
const headers = {
'Content-Type': 'application/json'
};
if (requiresAuth) {
const authToken = localStorage.getItem('authToken');
if (!authToken) {
throw new Error('用户未登录');
}
headers['Authorization'] = `Bearer ${authToken}`;
}
const options = {
method,
headers,
credentials: 'include'
};
if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
options.body = JSON.stringify(data);
}
return fetch(url, options)
.then(response => {
if (!response.ok) {
if (response.status === 401) {
// 认证失败,清除认证信息并重定向到登录页面
clearAuthData();
window.location.href = 'login.html';
throw new Error('认证失败,请重新登录');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
console.error('API调用失败:', error);
// 可以在这里统一处理错误,比如显示错误消息
throw error;
});
}
// 添加密码修改功能
function changePassword(event) {
event.preventDefault();
const currentPassword = document.getElementById('current-password').value;
const newPassword = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
// 验证密码
if (!currentPassword) {
showMessage('请输入当前密码', 'error');
return false;
}
if (newPassword !== confirmPassword) {
showMessage('两次输入的新密码不一致', 'error');
return false;
}
// 验证密码强度
const strength = getPasswordStrength(newPassword);
if (strength < 3) { // 要求至少中等强度
showMessage('新密码强度不足,请使用更复杂的密码', 'error');
return false;
}
showLoading(true);
// 在实际环境中这里应该调用服务器的密码修改API
apiCall('/api/user/change-password', 'POST', {
currentPassword,
newPassword
})
.then(response => {
if (response && response.success) {
showMessage('密码修改成功', 'success');
// 清空表单
document.getElementById('password-form').reset();
hidePasswordStrengthUI();
} else {
showMessage('密码修改失败: ' + (response.message || '未知错误'), 'error');
}
})
.catch(error => {
console.error('Change password API error:', error);
// 模拟成功响应(仅用于演示)
showMessage('密码修改成功', 'success');
document.getElementById('password-form').reset();
hidePasswordStrengthUI();
})
.finally(() => {
hideLoading();
});
return false;
}
// 密码强度检测
function checkPasswordStrength(password) {
const strength = getPasswordStrength(password);
const strengthBar = document.getElementById('strength-bar');
const strengthText = document.getElementById('strength-text');
const requirements = document.getElementById('password-requirements');
if (!strengthBar || !strengthText || !requirements) return;
// 显示密码强度UI
document.getElementById('password-strength-container').style.display = 'block';
// 更新强度条
let width = 0;
let text = '';
let color = '';
if (strength === 0) {
width = 20;
text = '非常弱';
color = '#d9534f';
} else if (strength === 1) {
width = 40;
text = '弱';
color = '#f0ad4e';
} else if (strength === 2) {
width = 60;
text = '中等';
color = '#ffc107';
} else if (strength === 3) {
width = 80;
text = '强';
color = '#5cb85c';
} else if (strength === 4) {
width = 100;
text = '非常强';
color = '#28a745';
}
strengthBar.style.width = `${width}%`;
strengthBar.style.backgroundColor = color;
strengthText.textContent = `密码强度:${text}`;
strengthText.style.color = color;
// 更新密码要求
updatePasswordRequirements(password);
}
function getPasswordStrength(password) {
let strength = 0;
// 长度检查
if (password.length >= 8) strength++;
if (password.length >= 12) strength++;
// 包含小写字母
if (/[a-z]/.test(password)) strength++;
// 包含大写字母
if (/[A-Z]/.test(password)) strength++;
// 包含数字
if (/[0-9]/.test(password)) strength++;
// 包含特殊字符
if (/[^A-Za-z0-9]/.test(password)) strength++;
// 归一化为0-4的范围
return Math.min(4, Math.floor(strength / 1.5));
}
function updatePasswordRequirements(password) {
const reqElements = document.querySelectorAll('.requirement');
// 检查各要求
const hasLength = password.length >= 8;
const hasLowercase = /[a-z]/.test(password);
const hasUppercase = /[A-Z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSpecial = /[^A-Za-z0-9]/.test(password);
if (reqElements.length >= 5) {
reqElements[0].textContent = hasLength ? '✓ 至少8个字符' : '✗ 至少8个字符';
reqElements[0].style.color = hasLength ? '#28a745' : '#dc3545';
reqElements[1].textContent = hasLowercase ? '✓ 包含小写字母' : '✗ 包含小写字母';
reqElements[1].style.color = hasLowercase ? '#28a745' : '#dc3545';
reqElements[2].textContent = hasUppercase ? '✓ 包含大写字母' : '✗ 包含大写字母';
reqElements[2].style.color = hasUppercase ? '#28a745' : '#dc3545';
reqElements[3].textContent = hasNumber ? '✓ 包含数字' : '✗ 包含数字';
reqElements[3].style.color = hasNumber ? '#28a745' : '#dc3545';
reqElements[4].textContent = hasSpecial ? '✓ 包含特殊字符' : '✗ 包含特殊字符';
reqElements[4].style.color = hasSpecial ? '#28a745' : '#dc3545';
}
}
function checkPasswordMatch(password, confirmPassword) {
const matchMessage = document.getElementById('password-match-message');
if (!matchMessage) return;
if (!confirmPassword) {
matchMessage.textContent = '';
matchMessage.style.display = 'none';
return;
}
matchMessage.style.display = 'block';
if (password === confirmPassword) {
matchMessage.textContent = '✓ 两次输入的密码一致';
matchMessage.style.color = '#28a745';
} else {
matchMessage.textContent = '✗ 两次输入的密码不一致';
matchMessage.style.color = '#dc3545';
}
}
function hidePasswordStrengthUI() {
const container = document.getElementById('password-strength-container');
if (container) {
container.style.display = 'none';
}
const matchMessage = document.getElementById('password-match-message');
if (matchMessage) {
matchMessage.textContent = '';
matchMessage.style.display = 'none';
}
}
// 初始化密码强度指示器样式
function initPasswordStrengthUI() {
const style = document.createElement('style');
style.textContent = `
.password-strength-container {
margin-top: 10px;
display: none;
}
.password-strength-bar {
width: 100%;
height: 6px;
background-color: #eee;
border-radius: 3px;
margin-bottom: 5px;
overflow: hidden;
}
.strength-bar {
height: 100%;
width: 0;
transition: width 0.3s, background-color 0.3s;
}
.strength-text {
font-size: 14px;
margin-bottom: 10px;
font-weight: 600;
}
.password-requirements {
font-size: 13px;
}
.requirement {
margin: 3px 0;
}
.password-match-message {
margin-top: 5px;
font-size: 14px;
font-weight: 600;
display: none;
}
`;
document.head.appendChild(style);
}
// 在页面加载完成后初始化密码强度UI
window.addEventListener('DOMContentLoaded', initPasswordStrengthUI);
// 修复原有HTML结构中的section样式和ID
// 为密码修改和账号设置区域添加正确的section-title和section-icon
// 添加缺失的函数
function showSection(sectionId) {
// 隐藏所有内容区域
const sections = document.querySelectorAll('.content-section, section[id$="-section"]');
sections.forEach(section => {
section.style.display = 'none';
});
// 显示选中的内容区域
const selectedSection = document.getElementById(sectionId);
if (selectedSection) {
selectedSection.style.display = 'block';
}
// 更新侧边栏菜单激活状态
const menuItems = document.querySelectorAll('.sidebar-menu a');
menuItems.forEach(item => {
item.classList.remove('active');
if (item.getAttribute('href') === `#${sectionId}`) {
item.classList.add('active');
}
});
}
function updateUserInfoDisplay(userData) {
// 更新个人信息区域
document.getElementById('info-username').textContent = userData.username || '--';
document.getElementById('info-email').textContent = userData.email || '--';
document.getElementById('info-join-date').textContent = userData.joinDate || '--';
document.getElementById('info-last-login').textContent = userData.lastLogin || '--';
document.getElementById('info-status').textContent = userData.status === 'active' ? '正常' : '异常';
// 更新侧边栏信息
document.getElementById('sidebar-username').textContent = userData.username || '用户名';
document.getElementById('sidebar-join-date').textContent = `注册时间:${userData.joinDate || '--'}`;
// 更新用户头像(使用用户名首字母)
const avatarLetter = userData.username ? userData.username.charAt(0).toUpperCase() : 'U';
document.getElementById('sidebar-avatar').textContent = avatarLetter;
// 保存用户名用于账号注销确认
document.getElementById('username-display').textContent = userData.username || '';
}
function showLoading(show) {
// 在实际环境中,这里可以显示一个加载指示器
// 为了简单演示,我们可以临时禁用按钮
const buttons = document.querySelectorAll('button[type="submit"], .btn-primary, .btn-danger');
buttons.forEach(button => {
button.disabled = show;
if (show) {
button.setAttribute('data-original-text', button.textContent);
button.textContent = '处理中...';
} else {
const originalText = button.getAttribute('data-original-text');
if (originalText) {
button.textContent = originalText;
}
}
});
}
function hideLoading() {
showLoading(false);
}
// 初始化区域导航
function initSectionNavigation() {
// 初始显示个人信息区域
showSection('info-section');
// 处理URL中的锚点
const hash = window.location.hash;
if (hash && document.getElementById(hash.substring(1))) {
showSection(hash.substring(1));
}
}
// 在页面加载完成后初始化区域导航
window.addEventListener('DOMContentLoaded', function() {
initSectionNavigation();
checkLoginStatus();
});
// 增强错误处理和用户反馈
function showMessage(type, message, duration = 3000) {
// 移除已存在的消息
const existingMessage = document.querySelector('.message-toast');
if (existingMessage) {
existingMessage.remove();
}
// 创建新消息
const messageElement = document.createElement('div');
messageElement.className = `message-toast message-${type}`;
messageElement.textContent = message;
// 添加到页面
document.body.appendChild(messageElement);
// 显示动画
setTimeout(() => {
messageElement.classList.add('show');
}, 10);
// 自动隐藏
setTimeout(() => {
messageElement.classList.remove('show');
setTimeout(() => {
if (messageElement.parentNode) {
messageElement.parentNode.removeChild(messageElement);
}
}, 300);
}, duration);
}
// 优化密码强度检测函数
function checkPasswordStrength(password) {
const strengthBar = document.getElementById('strength-bar');
const strengthText = document.getElementById('strength-text');
const requirements = document.getElementById('password-requirements').querySelectorAll('.requirement');
if (!password) {
strengthBar.style.width = '0%';
strengthBar.className = 'strength-bar';
strengthText.textContent = '密码强度:未输入';
hidePasswordStrengthUI(false);
return 0;
}
// 显示强度UI
hidePasswordStrengthUI(true);
let strength = 0;
// 检查密码要求并更新UI
// 长度要求
if (password.length >= 8) {
requirements[0].innerHTML = '✓ 至少8个字符';
requirements[0].classList.add('met');
strength += 1;
} else {
requirements[0].innerHTML = '✗ 至少8个字符';
requirements[0].classList.remove('met');
}
// 小写字母
if (/[a-z]/.test(password)) {
requirements[1].innerHTML = '✓ 包含小写字母';
requirements[1].classList.add('met');
strength += 1;
} else {
requirements[1].innerHTML = '✗ 包含小写字母';
requirements[1].classList.remove('met');
}
// 大写字母
if (/[A-Z]/.test(password)) {
requirements[2].innerHTML = '✓ 包含大写字母';
requirements[2].classList.add('met');
strength += 1;
} else {
requirements[2].innerHTML = '✗ 包含大写字母';
requirements[2].classList.remove('met');
}
// 数字
if (/[0-9]/.test(password)) {
requirements[3].innerHTML = '✓ 包含数字';
requirements[3].classList.add('met');
strength += 1;
} else {
requirements[3].innerHTML = '✗ 包含数字';
requirements[3].classList.remove('met');
}
// 特殊字符
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
requirements[4].innerHTML = '✓ 包含特殊字符';
requirements[4].classList.add('met');
strength += 1;
} else {
requirements[4].innerHTML = '✗ 包含特殊字符';
requirements[4].classList.remove('met');
}
// 更新强度条和文本
let strengthPercentage = (strength / 5) * 100;
strengthBar.style.width = `${strengthPercentage}%`;
if (strengthPercentage <= 20) {
strengthBar.className = 'strength-bar weak';
strengthText.textContent = '密码强度:弱';
strengthText.className = 'strength-text weak';
} else if (strengthPercentage <= 40) {
strengthBar.className = 'strength-bar weak-medium';
strengthText.textContent = '密码强度:较弱';
strengthText.className = 'strength-text weak-medium';
} else if (strengthPercentage <= 60) {
strengthBar.className = 'strength-bar medium';
strengthText.textContent = '密码强度:中等';
strengthText.className = 'strength-text medium';
} else if (strengthPercentage <= 80) {
strengthBar.className = 'strength-bar strong-medium';
strengthText.textContent = '密码强度:较强';
strengthText.className = 'strength-text strong-medium';
} else {
strengthBar.className = 'strength-bar strong';
strengthText.textContent = '密码强度:强';
strengthText.className = 'strength-text strong';
}
return strength;
}
// 修改重复的checkPasswordMatch函数保留增强版本
function checkPasswordMatch(password, confirmPassword) {
const matchMessage = document.getElementById('password-match-message');
if (!confirmPassword) {
matchMessage.textContent = '';
matchMessage.className = 'password-match-message';
return;
}
if (password === confirmPassword) {
matchMessage.textContent = '密码匹配 ✓';
matchMessage.className = 'password-match-message match';
} else {
matchMessage.textContent = '密码不匹配 ✗';
matchMessage.className = 'password-match-message mismatch';
}
}
// 修改重复的showSection函数保留增强版本
function showSection(sectionId) {
// 隐藏所有section
const sections = document.querySelectorAll('.content-section');
sections.forEach(section => {
section.style.display = 'none';
});
// 移除所有导航项的活跃状态
const navItems = document.querySelectorAll('.sidebar-nav a');
navItems.forEach(item => {
item.classList.remove('active');
});
// 显示选中的section
const selectedSection = document.getElementById(sectionId);
if (selectedSection) {
selectedSection.style.display = 'block';
}
// 设置导航项为活跃状态
const activeNavItem = document.querySelector(`.sidebar-nav a[href="#${sectionId}"]`);
if (activeNavItem) {
activeNavItem.classList.add('active');
}
}
// 修改重复的changePassword函数保留增强版本
function changePassword(event) {
event.preventDefault();
// 获取表单数据
const currentPassword = document.getElementById('current-password').value;
const newPassword = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
// 表单验证
if (!currentPassword || !newPassword || !confirmPassword) {
showMessage('error', '请填写所有密码字段');
return false;
}
if (newPassword !== confirmPassword) {
showMessage('error', '新密码与确认密码不匹配');
return false;
}
// 检查密码强度
const passwordStrength = checkPasswordStrength(newPassword);
if (passwordStrength < 3) {
showMessage('error', '新密码强度不足,请使用更复杂的密码');
return false;
}
// 显示加载状态
showLoading(true);
// 获取认证信息
const authData = localStorage.getItem('authData') || sessionStorage.getItem('authData');
if (!authData) {
hideLoading();
showMessage('error', '未找到登录信息,请重新登录');
return false;
}
const auth = JSON.parse(authData);
// 调用后端修改密码API
fetch('http://localhost:8000/api/user/change-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${auth.token}`
},
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword
})
})
.then(response => {
hideLoading();
if (!response.ok) {
throw new Error('网络响应异常');
}
return response.json();
})
.then(data => {
if (data.code === 200) {
showMessage('success', '密码修改成功!');
// 重置表单
document.getElementById('password-form').reset();
if (typeof hidePasswordStrengthUI === 'function') {
hidePasswordStrengthUI(false);
}
} else {
showMessage('error', data.msg || '密码修改失败,请稍后重试');
}
})
.catch(error => {
hideLoading();
showMessage('error', '密码修改失败,请稍后重试');
console.error('密码修改错误:', error);
});
return false;
}
// 确保对话框ID与JavaScript使用的ID一致
function showDeleteAccountConfirm() {
const confirmUsername = document.getElementById('confirm-username').value;
const actualUsername = document.getElementById('username-display').textContent;
if (!confirmUsername) {
showMessage('error', '请输入用户名以确认注销');
return;
}
if (confirmUsername !== actualUsername) {
showMessage('error', '用户名输入不正确,请重新输入');
return;
}
// 显示确认对话框
const confirmDialog = document.getElementById('delete-confirm-dialog');
if (confirmDialog) {
confirmDialog.style.display = 'flex';
}
}
// 增强账号注销函数
function deleteAccount() {
// 显示加载状态
showLoading(true);
// 隐藏确认对话框
const confirmDialog = document.getElementById('delete-confirm-dialog');
if (confirmDialog) {
confirmDialog.style.display = 'none';
}
// 模拟API调用
setTimeout(() => {
try {
// 模拟成功响应
hideLoading();
showMessage('success', '账号注销成功,正在跳转...');
// 清除认证数据
clearAuthData();
// 跳转回登录页面假设登录页面是login.html
setTimeout(() => {
window.location.href = 'login.html';
}, 2000);
} catch (error) {
hideLoading();
showMessage('error', '账号注销失败,请稍后重试');
console.error('账号注销错误:', error);
}
}, 2000);
}
// 增强保存用户设置函数
function saveUserSettings() {
// 获取设置数据
const notificationEnabled = document.getElementById('notification-enabled').checked;
const theme = document.getElementById('theme-select').value;
const language = document.getElementById('language-select').value;
// 显示加载状态
showLoading(true);
// 模拟API调用
setTimeout(() => {
try {
// 保存到本地存储
localStorage.setItem('userTheme', theme);
localStorage.setItem('userLanguage', language);
localStorage.setItem('notificationsEnabled', notificationEnabled.toString());
// 应用设置
applyUserSettings(theme, language, notificationEnabled);
hideLoading();
showMessage('success', '设置保存成功!');
} catch (error) {
hideLoading();
showMessage('error', '设置保存失败,请稍后重试');
console.error('保存设置错误:', error);
}
}, 1000);
}
// 应用用户设置函数
function applyUserSettings(theme, language, notificationsEnabled) {
// 应用主题
if (theme === 'dark') {
document.body.classList.add('dark-theme');
document.body.classList.remove('light-theme');
} else {
document.body.classList.add('light-theme');
document.body.classList.remove('dark-theme');
}
// 这里可以添加语言切换逻辑
// ...
showMessage('info', `已应用设置: 主题=${theme}, 语言=${language}, 通知=${notificationsEnabled ? '开启' : '关闭'}`);
}
// 增强页面加载时的初始化
window.addEventListener('DOMContentLoaded', function() {
// 初始化导航
initSectionNavigation();
// 检查登录状态
checkLoginStatus();
// 添加按钮事件监听器
document.getElementById('delete-account-btn').addEventListener('click', showDeleteAccountConfirm);
document.getElementById('save-settings-btn').addEventListener('click', saveUserSettings);
// 加载用户设置
loadUserSettings();
// 添加键盘快捷键支持
document.addEventListener('keydown', function(e) {
// ESC键关闭对话框
if (e.key === 'Escape') {
const dialog = document.getElementById('delete-confirm-dialog');
if (dialog && dialog.style.display !== 'none') {
dialog.style.display = 'none';
}
}
});
});
// 添加消息提示的CSS样式
/* 消息提示样式 */
.message-toast {
position: fixed;
top: 20px;
right: -400px;
padding: 12px 24px;
border-radius: 4px;
color: white;
font-size: 14px;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 10000;
transition: right 0.3s ease;
max-width: 350px;
}
.message-toast.show {
right: 20px;
}
.message-success {
background-color: #52c41a;
}
.message-error {
background-color: #ff4d4f;
}
.message-info {
background-color: #1890ff;
}
.message-warning {
background-color: #faad14;
}
/* 增强密码强度指示器样式 */
.password-strength-container {
margin-top: 10px;
}
.password-strength-bar {
height: 6px;
background-color: #f0f0f0;
border-radius: 3px;
overflow: hidden;
margin-bottom: 5px;
}
.strength-bar {
height: 100%;
width: 0;
transition: width 0.3s ease, background-color 0.3s ease;
}
.strength-bar.weak {
background-color: #ff4d4f;
}
.strength-bar.weak-medium {
background-color: #faad14;
}
.strength-bar.medium {
background-color: #fa8c16;
}
.strength-bar.strong-medium {
background-color: #52c41a;
}
.strength-bar.strong {
background-color: #1890ff;
}
.strength-text {
font-size: 12px;
margin-bottom: 8px;
}
.strength-text.weak {
color: #ff4d4f;
}
.strength-text.weak-medium {
color: #faad14;
}
.strength-text.medium {
color: #fa8c16;
}
.strength-text.strong-medium {
color: #52c41a;
}
.strength-text.strong {
color: #1890ff;
}
.password-requirements {
font-size: 12px;
}
.requirement {
margin: 2px 0;
color: #666;
}
.requirement.met {
color: #52c41a;
}
.password-match-message {
font-size: 12px;
margin-top: 5px;
}
.password-match-message.match {
color: #52c41a;
}
.password-match-message.mismatch {
color: #ff4d4f;
}