Files
yixiaogao/frontend/user-center.html
2025-11-27 18:32:24 +08:00

1753 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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;
}