document.addEventListener('DOMContentLoaded', () => { const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { themeToggle.addEventListener('click', () => { document.body.classList.toggle('dark-mode'); themeToggle.innerHTML = document.body.classList.contains('dark-mode') ? '' : ''; }); } // 初始化用户管理相关的功能 initUserManagement(); // 添加用户表单提交 document.getElementById('addUserForm').addEventListener('submit', async function(e) { e.preventDefault(); const userData = { username: document.getElementById('name').value.trim(), email: document.getElementById('email').value.trim(), password: document.getElementById('password').value, phone_number: document.getElementById('phoneNumber').value.trim() || null, role: document.getElementById('role').value, is_active: false, email_verified: false, phone_verified: false }; try { // 前端验证 validateUserData(userData); const token = localStorage.getItem('access_token'); const response = await fetch(`${API_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(userData) }); if (!response.ok) { const errorData = await response.json(); console.log('服务器返回的完整错误信息:', JSON.stringify(errorData, null, 2)); if (Array.isArray(errorData.detail)) { const firstError = errorData.detail[0]; throw new Error(firstError?.msg || lang[selectedLang].addUserFailed); } throw new Error(errorData.detail || lang[selectedLang].addUserFailed); } fetchUsers(); closeAddUserModal(); this.reset(); // 重置表单 showToast(lang[selectedLang].addUserSuccess, 'success'); } catch (error) { console.error(lang[selectedLang].addUserError, error); showToast(error.message || lang[selectedLang].addUserFailed, 'error'); } }); // 初始化用户管理相关的功能 initUserManagement(); }); function initUserManagement() { // 初始化用户管理相关的功能 checkLoginStatus(); showFeature(); } const API_URL = 'https://user.obscura.work/user'; let currentUser = null; // 获取用户信息的函数 async function fetchUserInfo() { try { const token = localStorage.getItem('access_token'); if (!token) { throw new Error('No access token found'); } const response = await makeAuthenticatedRequest(`${API_URL}/me`); if (!response.ok) { throw new Error('Failed to fetch user info'); } const userInfo = await response.json(); return userInfo; // 直接返回用户信息 } catch (error) { console.error('Error fetching user info:', error); logout(); return null; } } // 更新用户显示的函数 function updateUserDisplay() { const userName = document.getElementById('userName'); const userAvatar = document.getElementById('userAvatar'); const dashboardContent = document.getElementById('dashboardContent'); const usersContent = document.getElementById('usersContent'); if (currentUser) { userName.textContent = currentUser.username; userAvatar.textContent = currentUser.username.charAt(0).toUpperCase(); if (dashboardContent) dashboardContent.style.display = 'block'; if (usersContent) usersContent.style.display = 'none'; if (userCenterContent) userCenterContent.style.display = 'none'; } else { userName.textContent = 'Guest'; userAvatar.textContent = 'G'; if (dashboardContent) dashboardContent.style.display = 'none'; if (usersContent) usersContent.style.display = 'none'; if (userCenterContent) userCenterContent.style.display = 'none'; } } // 登出函数 async function logout() { const token = localStorage.getItem('access_token'); if (token) { try { await fetch(`${API_URL}/logout`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, }, }); } catch (error) { console.error(lang[selectedLang].logoutError, error); } } localStorage.removeItem('access_token'); currentUser = null; updateUserDisplay(); window.location.href = 'login.html'; // 重定向到登录页面 } // 显示仪表板 async function showDashboard() { try { const userInfo = await fetchUserInfo(); if (!userInfo) { alert(lang[selectedLang].pleaseLoginToViewDashboard); window.location.href = 'login.html'; // 重定向到登录页面 return; } // 检查邮箱验证状态 const emailVerified = userInfo.email_verified === 'True' || userInfo.email_verified === true; console.log('Email Verified:', emailVerified); // 打印邮箱验证状态 if (!emailVerified) { alert(lang[selectedLang].pleaseVerifyEmail); showUserCenterSection('profile'); // 跳转到个人资料页面 return; } // 如果用户已登录且邮箱已验证,继续显示仪表盘 const pageTitle = document.getElementById('pageTitle'); const dashboardContent = document.getElementById('dashboardContent'); const usersContent = document.getElementById('usersContent'); const chatContent = document.getElementById('chatContent'); const featureContent = document.getElementById('featureContent'); const userCenterContent = document.getElementById('userCenterContent'); const showApiDocs = document.getElementById('showApiDocs'); const apiDocsContent = document.getElementById('apiDocsContent'); ; if (!userInfo) { console.error('No user info found'); return; } if (apiDocsContent) apiDocsContent.style.display = 'none' if (showApiDocs) showApiDocs.style.display = 'none'; if (userCenterContent) userCenterContent.style.display = 'none'; if (chatContent) chatContent.style.display = 'none'; if (featureContent) featureContent.style.display = 'none'; if (usersContent) usersContent.style.display = 'none'; if (pageTitle) pageTitle.textContent = lang[selectedLang].dashboard; if (dashboardContent) { dashboardContent.style.display = 'block'; dashboardContent.innerHTML = `

Hello, ${userInfo.username}!

Total Users

Loading...

New Users Today

Loading...

Active Users

Loading...

Recent New Users

`; fetchDashboardData(); } } catch (error) { console.error('Error in showDashboard:', error); alert(lang[selectedLang].errorGettingUserInfo); } } // 获取仪表板数据 async function fetchDashboardData() { try { const response = await makeAuthenticatedRequest(`${API_URL}/dashboard`); if (!response.ok) { throw new Error(lang[selectedLang].fetchDashboardDataFailed); } const data = await response.json(); updateDashboardUI(data); fetchUserStats(); } catch (error) { console.error('Error fetching dashboard data:', error); alert(lang[selectedLang].fetchDashboardDataFailed); } } async function fetchUserStats() { try { const response = await makeAuthenticatedRequest(`${API_URL}/user-stats`); if (!response.ok) { throw new Error('Failed to fetch user stats'); } const data = await response.json(); createUserChart(data); } catch (error) { console.error('Error fetching user stats:', error); } } // 更新仪表板UI function updateDashboardUI(data) { try { // 更新用户统计数据 const totalUsersEl = document.getElementById('totalUsers'); const newUsersEl = document.getElementById('newUsers'); const activeUsersEl = document.getElementById('activeUsers'); if (totalUsersEl) totalUsersEl.textContent = data.total_users; if (newUsersEl) newUsersEl.textContent = data.new_users_today; if (activeUsersEl) activeUsersEl.textContent = data.active_users; // 更新最近用户列表 const activityList = document.getElementById('activityList'); if (activityList) { activityList.innerHTML = ''; if (data.recent_users && Array.isArray(data.recent_users) && data.recent_users.length > 0) { data.recent_users.forEach(user => { const li = document.createElement('li'); li.className = 'activity-item'; li.innerHTML = `

${lang[selectedLang].newUser}: ${user.username}

${formatActivityTime(user.created_at)}

`; activityList.appendChild(li); }); } else { console.warn(lang[selectedLang].noRecentUsersData); activityList.innerHTML = `
  • ${lang[selectedLang].noRecentUserActivities}
  • `; } } else { console.error(lang[selectedLang].activityListNotFound); } } catch (error) { console.error('Error updating dashboard UI:', error); } } // 格式化活动时间 function formatActivityTime(timestamp) { const date = new Date(timestamp); const now = new Date(); const diffTime = Math.abs(now - date); const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); const diffHours = Math.floor(diffTime / (1000 * 60 * 60)); const diffMinutes = Math.floor(diffTime / (1000 * 60)); if (diffDays > 0) { return `${diffDays} ${lang[selectedLang].daysAgo}`; } else if (diffHours > 0) { return `${diffHours} ${lang[selectedLang].hoursAgo}`; } else if (diffMinutes > 0) { return `${diffMinutes} ${lang[selectedLang].minutesAgo}`; } else { return lang[selectedLang].justNow; } } // 创建用户统计图表 function createUserChart(userStats) { const ctx = document.getElementById('userChart').getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: userStats.dates, datasets: [{ label: lang[selectedLang].newUsersToday, data: userStats.daily_new_users, backgroundColor: 'rgba(102, 126, 234, 0.2)', borderColor: 'rgba(102, 126, 234, 1)', borderWidth: 2, pointBackgroundColor: 'rgba(102, 126, 234, 1)', pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', pointHoverBorderColor: 'rgba(102, 126, 234, 1)' }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, title: { display: true, text: lang[selectedLang].numberOfNewUsers } }, x: { title: { display: true, text: lang[selectedLang].date } } } } }); } // 显示用户列表 let currentPage = 1; const usersPerPage = 10; // 每页显示的用户数量 let allUsers = []; // 存储所有用户数据 // 在文件开头添加变量 let searchUserTimeout = null; // 修改 showUsers 函数,添加搜索框初始化 async function showUsers() { try { const userInfo = await fetchUserInfo(); if (!userInfo) { alert(lang[selectedLang].pleaseLoginToViewUserList); window.location.href = 'login.html'; return; } // 检查邮箱验证状态 const emailVerified = userInfo.email_verified === 'True' || userInfo.email_verified === true; if (!emailVerified) { alert(lang[selectedLang].pleaseCompleteEmailVerification); showUserCenterSection('profile'); return; } // 更新页面内容 const pageTitle = document.getElementById('pageTitle'); const dashboardContent = document.getElementById('dashboardContent'); const usersContent = document.getElementById('usersContent'); const chatContent = document.getElementById('chatContent'); const featureContent = document.getElementById('featureContent'); const userCenterContent = document.getElementById('userCenterContent'); const apiDocsContent = document.getElementById('apiDocsContent'); if (apiDocsContent) apiDocsContent.style.display = 'none'; if (dashboardContent) dashboardContent.style.display = 'none'; if (featureContent) featureContent.style.display = 'none'; if (usersContent) usersContent.style.display = 'block'; if (chatContent) chatContent.style.display = 'none'; if (userCenterContent) userCenterContent.style.display = 'none'; if (pageTitle) pageTitle.innerText = lang[selectedLang].userManagement; // 初始化搜索功能 initializeUserSearch(); fetchUsers(); } catch (error) { console.error('Error in showUsers:', error); alert(lang[selectedLang].errorGettingUserInfo); } } // 修改搜索初始化函数 function initializeUserSearch() { const searchInput = document.getElementById('userSearch'); const searchIcon = document.querySelector('.user-search-icon'); if (searchInput) { // 添加占位符提示 searchInput.placeholder = '搜索用户名、邮箱或角色(admin/user)...'; // 输入事件处理(实时搜索) searchInput.addEventListener('input', function(e) { if (searchUserTimeout) { clearTimeout(searchUserTimeout); } searchUserTimeout = setTimeout(() => { searchUsers(e.target.value); }, 300); }); // 回车键事件处理 searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { e.preventDefault(); searchUsers(this.value); } }); // 点击搜索图标事件处理 if (searchIcon) { searchIcon.addEventListener('click', function() { searchUsers(searchInput.value); }); searchIcon.style.cursor = 'pointer'; } } } // 修改搜索用户函数 async function searchUsers(query) { try { const token = localStorage.getItem('access_token'); const response = await fetch(`${API_URL}/api/users-search?query=${encodeURIComponent(query)}`, { method: 'GET', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('搜索失败'); } const searchResults = await response.json(); allUsers = searchResults; // 更新全局用户列表 currentPage = 1; // 重置到第一页 updatePagination(); displayUsers(); // 如果没有搜索结果,显示提示 if (searchResults.length === 0) { showToast('未找到匹配的用户', 'info'); } } catch (error) { console.error('搜索用户时出错:', error); showToast(error.message || lang[selectedLang].searchFailed, 'error'); } } // 获取用户列表 async function fetchUsers() { try { const response = await makeAuthenticatedRequest(`${API_URL}/api/users`); if (!response.ok) { throw new Error('Network response was not ok'); } allUsers = await response.json(); updatePagination(); displayUsers(); return allUsers; } catch (error) { console.error('Error fetching users:', error); alert('Failed to fetch users. Please try again.'); return []; } } // 更新分页信息 function updatePagination() { const totalPages = Math.ceil(allUsers.length / usersPerPage); const paginationDiv = document.querySelector('.pagination'); let paginationHTML = ` < `; const showPages = (start, end) => { for (let i = start; i <= end; i++) { paginationHTML += ` ${i} `; } }; if (totalPages <= 7) { showPages(1, totalPages); } else { if (currentPage <= 3) { showPages(1, 5); paginationHTML += `...`; paginationHTML += `${totalPages}`; } else if (currentPage >= totalPages - 2) { paginationHTML += `1`; paginationHTML += `...`; showPages(totalPages - 4, totalPages); } else { paginationHTML += `1`; paginationHTML += `...`; showPages(currentPage - 1, currentPage + 1); paginationHTML += `...`; paginationHTML += `${totalPages}`; } } paginationHTML += ` > `; paginationDiv.innerHTML = paginationHTML; } // 切换到指定页面 function goToPage(page) { currentPage = page; displayUsers(); updatePagination(); } // 修改changePage函数 function changePage(direction) { const totalPages = Math.ceil(allUsers.length / usersPerPage); currentPage += direction; if (currentPage < 1) currentPage = 1; if (currentPage > totalPages) currentPage = totalPages; displayUsers(); updatePagination(); } // 修改显示用户函数 function displayUsers() { const start = (currentPage - 1) * usersPerPage; const end = start + usersPerPage; const pageUsers = allUsers.slice(start, end); const userTableBody = document.getElementById('userTableBody'); userTableBody.innerHTML = ''; if (pageUsers.length === 0) { // 果没有用户数据,显示一个提示行 const emptyRow = document.createElement('tr'); emptyRow.innerHTML = ` ${lang[selectedLang].noUsersFound} `; userTableBody.appendChild(emptyRow); return; } pageUsers.forEach((user, index) => { const row = document.createElement('tr'); row.innerHTML = ` ${(currentPage - 1) * usersPerPage + index + 1} ${user.username} ${user.email} ${user.role} `; userTableBody.appendChild(row); }); } // 修改编辑用户函数 async function editUser(id) { try { // 获取当前用户信息 const userResponse = await makeAuthenticatedRequest(`${API_URL}/api/users/${id}`); if (!userResponse.ok) { throw new Error(lang[selectedLang].getUserInfoFailed); } const userData = await userResponse.json(); // 创建编辑表单 const formContent = `
    `; // 使用自定义对话框显示表单 const result = await showCustomDialog( lang[selectedLang].editUser, formContent, lang[selectedLang].save, lang[selectedLang].cancel ); if (result && result.confirmed) { const { username, role } = result.formData; if (!username) { throw new Error(lang[selectedLang].usernameRequired); } const response = await makeAuthenticatedRequest(`${API_URL}/edit/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, role }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.detail || lang[selectedLang].updateUserFailed); } await fetchUsers(); // 重新获取用户列表 showToast(lang[selectedLang].userInfoUpdateSuccess, 'success'); } } catch (error) { console.error('Error editing user:', error); showToast(error.message || lang[selectedLang].updateUserFailed, 'error'); } } // 修改自定义对话框函数 function showCustomDialog(title, content, confirmText, cancelText) { return new Promise((resolve) => { const dialog = document.createElement('div'); dialog.className = 'custom-dialog'; dialog.innerHTML = `

    ${title}

    ${content}
    `; document.body.appendChild(dialog); const confirmBtn = dialog.querySelector('.btn-confirm'); const cancelBtn = dialog.querySelector('.btn-cancel'); confirmBtn.addEventListener('click', () => { const username = dialog.querySelector('#edit-username')?.value; const role = dialog.querySelector('#edit-role')?.value; document.body.removeChild(dialog); resolve({ confirmed: true, formData: { username, role } }); }); cancelBtn.addEventListener('click', () => { document.body.removeChild(dialog); resolve({ confirmed: false }); }); }); } // 修改显示提示消息函数 function showToast(message, type = 'info') { try { // 移除已存在的提示框 const existingToast = document.querySelector('.toast-container'); if (existingToast && existingToast.parentNode) { existingToast.parentNode.removeChild(existingToast); } // 创建提示框容器 const toastContainer = document.createElement('div'); toastContainer.className = 'toast-container'; // 创建提示框 const toast = document.createElement('div'); toast.className = `toast toast-${type}`; // 添加图标 const icon = document.createElement('i'); switch (type) { case 'success': icon.className = 'fas fa-check-circle'; break; case 'error': icon.className = 'fas fa-exclamation-circle'; break; case 'info': icon.className = 'fas fa-info-circle'; break; case 'warning': icon.className = 'fas fa-exclamation-triangle'; break; } // 创建消息文本元素 const messageText = document.createElement('span'); messageText.textContent = message; // 组装提示框 toast.appendChild(icon); toast.appendChild(messageText); toastContainer.appendChild(toast); document.body.appendChild(toastContainer); // 添加显示动画 setTimeout(() => { toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => { if (toastContainer && toastContainer.parentNode) { toastContainer.parentNode.removeChild(toastContainer); } }, 300); }, 3000); }, 100); } catch (error) { console.error('Error showing toast:', error); } } // 修改删除用户函数 async function deleteUser(id) { try { const confirmed = await showCustomDialog( lang[selectedLang].confirmDelete, `

    ${lang[selectedLang].confirmDeleteUserMessage}

    `, lang[selectedLang].delete, lang[selectedLang].cancel ); if (confirmed) { const response = await makeAuthenticatedRequest(`${API_URL}/api/users/${id}`, { method: 'DELETE' }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.detail || lang[selectedLang].deleteUserFailed); } await fetchUsers(); // 重新获取用户列表 showToast(lang[selectedLang].userDeleteSuccess, 'success'); } } catch (error) { console.error('Error deleting user:', error); showToast(error.message || lang[selectedLang].deleteUserFailed, 'error'); } } // 打开添加用户模态框 function openAddUserModal() { const modal = document.getElementById('addUserModal'); const form = document.getElementById('addUserForm'); form.reset(); // 确保表单是空的 modal.style.display = 'block'; } // 关闭添加用户模态框 function closeAddUserModal() { const modal = document.getElementById('addUserModal'); const form = document.getElementById('addUserForm'); modal.style.display = 'none'; form.reset(); // 重置表单 } async function makeAuthenticatedRequest(url, options = {}) { const token = localStorage.getItem('access_token'); if (!token) { throw new Error('未找到token'); } const headers = new Headers(options.headers || {}); headers.append('Authorization', `Bearer ${token}`); const response = await fetch(url, { ...options, headers }); if (response.status === 401) { logout(); throw new Error('未授权'); } return response; } // 导出用户数据为CSV function exportUserData() { if (!allUsers || allUsers.length === 0) { alert(lang[selectedLang].noUserDataToExport); return; } const csvContent = "data:text/csv;charset=utf-8," + "用户名,邮箱,角色\n" + allUsers.map(user => `${user.username},${user.email},${user.role}`).join("\n"); const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", "user_data.csv"); document.body.appendChild(link); link.click(); document.body.removeChild(link); } // 添加数据验证函数 function validateUserData(userData) { // 用户名验证:只验证必填 if (!userData.username || userData.username.trim() === '') { throw new Error('用户名不能为空'); } // 邮箱验证:必填且唯一,格式正确 if (!userData.email || !userData.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { throw new Error('请输入有效的邮箱地址'); } // 密码验证 if (!userData.password || userData.password.length < 6) { throw new Error('密码至少需要6个字符'); } // 手机号验证:如果提供则必须格式正确 if (userData.phone_number && userData.phone_number !== '') { if (!userData.phone_number.match(/^\d{11}$/)) { throw new Error('请输入有效的手机号码'); } } }