373 lines
14 KiB
JavaScript
373 lines
14 KiB
JavaScript
// 用户个人中心页面交互
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 获取表单对象
|
|
const profileForm = document.getElementById('profileForm');
|
|
const passwordForm = document.getElementById('passwordForm');
|
|
|
|
// 表单验证逻辑
|
|
if (profileForm) {
|
|
profileForm.addEventListener('submit', function(event) {
|
|
let valid = true;
|
|
|
|
// 清除之前的错误提示
|
|
clearValidationErrors();
|
|
|
|
// 验证邮箱
|
|
const emailField = document.getElementById('email');
|
|
if (emailField.value.trim() !== '') {
|
|
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailPattern.test(emailField.value.trim())) {
|
|
showError(emailField, '请输入有效的邮箱地址');
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
// 验证手机号
|
|
const phoneField = document.getElementById('phone');
|
|
if (phoneField.value.trim() !== '') {
|
|
const phonePattern = /^1[3456789]\d{9}$/;
|
|
if (!phonePattern.test(phoneField.value.trim())) {
|
|
showError(phoneField, '请输入有效的手机号码');
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
if (!valid) {
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 密码修改表单验证
|
|
if (passwordForm) {
|
|
passwordForm.addEventListener('submit', function(event) {
|
|
let valid = true;
|
|
|
|
// 清除之前的错误提示
|
|
clearValidationErrors();
|
|
|
|
// 验证当前密码
|
|
const currentPasswordField = document.getElementById('current_password');
|
|
if (currentPasswordField.value.trim() === '') {
|
|
showError(currentPasswordField, '请输入当前密码');
|
|
valid = false;
|
|
}
|
|
|
|
// 验证新密码
|
|
const newPasswordField = document.getElementById('new_password');
|
|
if (newPasswordField.value.trim() === '') {
|
|
showError(newPasswordField, '请输入新密码');
|
|
valid = false;
|
|
} else if (newPasswordField.value.length < 6) {
|
|
showError(newPasswordField, '密码长度至少为6个字符');
|
|
valid = false;
|
|
}
|
|
|
|
// 验证确认密码
|
|
const confirmPasswordField = document.getElementById('confirm_password');
|
|
if (confirmPasswordField.value.trim() === '') {
|
|
showError(confirmPasswordField, '请确认新密码');
|
|
valid = false;
|
|
} else if (confirmPasswordField.value !== newPasswordField.value) {
|
|
showError(confirmPasswordField, '两次输入的密码不一致');
|
|
valid = false;
|
|
}
|
|
|
|
if (!valid) {
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 获取用户统计数据
|
|
fetchUserStats();
|
|
|
|
// 获取用户活动记录
|
|
const activityFilter = document.getElementById('activityFilter');
|
|
if (activityFilter) {
|
|
// 初始加载
|
|
fetchUserActivities('all');
|
|
|
|
// 监听过滤器变化
|
|
activityFilter.addEventListener('change', function() {
|
|
fetchUserActivities(this.value);
|
|
});
|
|
}
|
|
|
|
// 处理URL中的tab参数
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const tabParam = urlParams.get('tab');
|
|
if (tabParam) {
|
|
const tabElement = document.getElementById(`${tabParam}-tab`);
|
|
if (tabElement) {
|
|
$('#profileTabs a[href="#' + tabParam + '"]').tab('show');
|
|
}
|
|
}
|
|
|
|
// 清除表单验证错误
|
|
function clearValidationErrors() {
|
|
const invalidFields = document.querySelectorAll('.is-invalid');
|
|
const feedbackElements = document.querySelectorAll('.invalid-feedback');
|
|
|
|
invalidFields.forEach(field => {
|
|
field.classList.remove('is-invalid');
|
|
});
|
|
|
|
feedbackElements.forEach(element => {
|
|
element.parentNode.removeChild(element);
|
|
});
|
|
}
|
|
|
|
// 显示错误消息
|
|
function showError(field, message) {
|
|
field.classList.add('is-invalid');
|
|
|
|
const feedback = document.createElement('div');
|
|
feedback.className = 'invalid-feedback';
|
|
feedback.innerText = message;
|
|
|
|
field.parentNode.appendChild(feedback);
|
|
}
|
|
|
|
// 获取用户统计数据
|
|
function fetchUserStats() {
|
|
fetch('/user/api/stats')
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
updateUserStats(data);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching user stats:', error);
|
|
// 出错时使用默认值
|
|
updateUserStats({borrow: 0, returned: 0, overdue: 0});
|
|
});
|
|
}
|
|
|
|
// 更新用户统计显示
|
|
function updateUserStats(data) {
|
|
const borrowCount = document.getElementById('borrowCount');
|
|
const returnedCount = document.getElementById('returnedCount');
|
|
const overdueCount = document.getElementById('overdueCount');
|
|
|
|
if (borrowCount) borrowCount.textContent = data.borrow;
|
|
if (returnedCount) returnedCount.textContent = data.returned;
|
|
if (overdueCount) overdueCount.textContent = data.overdue;
|
|
}
|
|
|
|
// 获取用户活动记录
|
|
function fetchUserActivities(type) {
|
|
const timelineContainer = document.getElementById('activityTimeline');
|
|
if (!timelineContainer) return;
|
|
|
|
// 显示加载中
|
|
timelineContainer.innerHTML = `
|
|
<div class="timeline-loading">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="sr-only">Loading...</span>
|
|
</div>
|
|
<p>加载中...</p>
|
|
</div>
|
|
`;
|
|
|
|
fetch(`/user/api/activities?type=${type}`)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
renderActivityTimeline(data.activities, timelineContainer);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching activities:', error);
|
|
timelineContainer.innerHTML = '<div class="text-center p-4">获取活动记录失败</div>';
|
|
});
|
|
}
|
|
|
|
// 渲染活动时间线
|
|
function renderActivityTimeline(activities, container) {
|
|
if (!activities || activities.length === 0) {
|
|
container.innerHTML = '<div class="text-center p-4">暂无活动记录</div>';
|
|
return;
|
|
}
|
|
|
|
let timelineHTML = '';
|
|
|
|
activities.forEach((activity, index) => {
|
|
let iconClass = 'fas fa-info';
|
|
|
|
if (activity.type === 'login') {
|
|
iconClass = 'fas fa-sign-in-alt';
|
|
} else if (activity.type === 'borrow') {
|
|
iconClass = 'fas fa-book';
|
|
} else if (activity.type === 'return') {
|
|
iconClass = 'fas fa-undo';
|
|
} else if (activity.type === 'other') {
|
|
iconClass = 'fas fa-cog';
|
|
}
|
|
|
|
const isLast = index === activities.length - 1;
|
|
|
|
timelineHTML += `
|
|
<div class="timeline-item ${isLast ? 'last' : ''} timeline-type-${activity.type}">
|
|
<div class="timeline-icon">
|
|
<i class="${iconClass}"></i>
|
|
</div>
|
|
<div class="timeline-content">
|
|
<div class="timeline-header">
|
|
<h5 class="timeline-title">${activity.title}</h5>
|
|
<div class="timeline-time">${activity.time}</div>
|
|
</div>
|
|
<div class="timeline-details">
|
|
${activity.details}
|
|
${activity.ip ? `<div class="text-muted small">IP: ${activity.ip}</div>` : ''}
|
|
${activity.target_type && activity.target_id ? `
|
|
<div class="text-muted small">
|
|
${activity.target_type === 'book' ? '图书ID: ' : '目标ID: '}${activity.target_id}
|
|
</div>` : ''}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
container.innerHTML = timelineHTML;
|
|
}
|
|
|
|
// 添加分页加载更多功能
|
|
let currentPage = 1;
|
|
|
|
function loadMoreActivities(type) {
|
|
currentPage++;
|
|
const timelineContainer = document.getElementById('activityTimeline');
|
|
|
|
// 显示加载中指示器
|
|
const loadingIndicator = document.createElement('div');
|
|
loadingIndicator.className = 'text-center mt-3 mb-3';
|
|
loadingIndicator.id = 'loadingMoreIndicator';
|
|
loadingIndicator.innerHTML = `
|
|
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
|
<span class="sr-only">Loading...</span>
|
|
</div>
|
|
<span class="ml-2">加载更多...</span>
|
|
`;
|
|
|
|
timelineContainer.appendChild(loadingIndicator);
|
|
|
|
fetch(`/user/api/activities?type=${type}&page=${currentPage}`)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
// 移除加载指示器
|
|
const indicator = document.getElementById('loadingMoreIndicator');
|
|
if (indicator) indicator.remove();
|
|
|
|
if (data.activities && data.activities.length > 0) {
|
|
// 添加新活动到现有时间线
|
|
appendActivitiesToTimeline(data.activities, timelineContainer);
|
|
|
|
// 如果已经是最后一页,隐藏加载更多按钮
|
|
if (data.current_page >= data.pages) {
|
|
const loadMoreBtn = document.getElementById('loadMoreBtn');
|
|
if (loadMoreBtn) loadMoreBtn.style.display = 'none';
|
|
}
|
|
} else {
|
|
// 没有更多活动
|
|
const noMoreDiv = document.createElement('div');
|
|
noMoreDiv.className = 'text-center p-3 text-muted';
|
|
noMoreDiv.textContent = '没有更多活动记录';
|
|
timelineContainer.appendChild(noMoreDiv);
|
|
|
|
const loadMoreBtn = document.getElementById('loadMoreBtn');
|
|
if (loadMoreBtn) loadMoreBtn.style.display = 'none';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading more activities:', error);
|
|
// 移除加载指示器
|
|
const indicator = document.getElementById('loadingMoreIndicator');
|
|
if (indicator) indicator.remove();
|
|
|
|
// 显示错误消息
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.className = 'text-center p-3 text-danger';
|
|
errorDiv.textContent = '加载更多活动失败';
|
|
timelineContainer.appendChild(errorDiv);
|
|
});
|
|
}
|
|
|
|
// 将新活动附加到现有时间线
|
|
function appendActivitiesToTimeline(activities, container) {
|
|
// 移除之前的 "last" 类
|
|
const lastItems = container.querySelectorAll('.timeline-item.last');
|
|
lastItems.forEach(item => {
|
|
item.classList.remove('last');
|
|
});
|
|
|
|
activities.forEach((activity, index) => {
|
|
let iconClass = 'fas fa-info';
|
|
|
|
if (activity.type === 'login') {
|
|
iconClass = 'fas fa-sign-in-alt';
|
|
} else if (activity.type === 'borrow') {
|
|
iconClass = 'fas fa-book';
|
|
} else if (activity.type === 'return') {
|
|
iconClass = 'fas fa-undo';
|
|
} else if (activity.type === 'other') {
|
|
iconClass = 'fas fa-cog';
|
|
}
|
|
|
|
const isLast = index === activities.length - 1;
|
|
|
|
const timelineItem = document.createElement('div');
|
|
timelineItem.className = `timeline-item ${isLast ? 'last' : ''} timeline-type-${activity.type}`;
|
|
|
|
timelineItem.innerHTML = `
|
|
<div class="timeline-icon">
|
|
<i class="${iconClass}"></i>
|
|
</div>
|
|
<div class="timeline-content">
|
|
<div class="timeline-header">
|
|
<h5 class="timeline-title">${activity.title}</h5>
|
|
<div class="timeline-time">${activity.time}</div>
|
|
</div>
|
|
<div class="timeline-details">
|
|
${activity.details}
|
|
${activity.ip ? `<div class="text-muted small">IP: ${activity.ip}</div>` : ''}
|
|
${activity.target_type && activity.target_id ? `
|
|
<div class="text-muted small">
|
|
${activity.target_type === 'book' ? '图书ID: ' : '目标ID: '}${activity.target_id}
|
|
</div>` : ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
container.appendChild(timelineItem);
|
|
});
|
|
|
|
// 如果显示了加载更多按钮,确保它在末尾
|
|
const loadMoreBtn = document.getElementById('loadMoreBtn');
|
|
if (loadMoreBtn) {
|
|
container.appendChild(loadMoreBtn);
|
|
}
|
|
}
|
|
|
|
// 添加加载更多按钮点击事件
|
|
document.body.addEventListener('click', function(e) {
|
|
if (e.target && e.target.id === 'loadMoreBtn') {
|
|
const activityFilter = document.getElementById('activityFilter');
|
|
loadMoreActivities(activityFilter ? activityFilter.value : 'all');
|
|
}
|
|
});
|
|
});
|