// ==================================================================== // 登录验证 // ==================================================================== (function () { const ACCESS_KEY = 'oadmin123'; const overlay = document.getElementById('loginOverlay'); if (!overlay) return; if (sessionStorage.getItem('authed') === '1') { overlay.classList.add('hidden'); return; } const pwdInput = document.getElementById('loginPassword'); const loginBtn = document.getElementById('loginBtn'); const errEl = document.getElementById('loginError'); function doLogin() { if (pwdInput.value === ACCESS_KEY) { sessionStorage.setItem('authed', '1'); overlay.classList.add('hidden'); errEl.textContent = ''; } else { errEl.textContent = '密码错误,请重试'; pwdInput.value = ''; pwdInput.focus(); } } loginBtn.addEventListener('click', doLogin); pwdInput.addEventListener('keydown', e => { if (e.key === 'Enter') doLogin(); }); pwdInput.focus(); })(); // 账号管理页面JavaScript class AdminManager { constructor() { this.isAuthenticated = false; this.token = ''; // 分页与搜索状态 this.page = 1; this.pageSize = 10; this.total = 0; this.query = ''; this.init(); } init() { this.bindEvents(); this.checkStoredAuth(); } bindEvents() { // 登录表单 document.getElementById('loginForm').addEventListener('submit', this.handleLogin.bind(this)); // 刷新账号列表 document.getElementById('refreshAccountsBtn').addEventListener('click', () => { this.page = 1; this.loadAccounts(); }); // 搜索 const searchInput = document.getElementById('accountSearchInput'); const searchBtn = document.getElementById('searchAccountsBtn'); if (searchBtn) { searchBtn.addEventListener('click', () => { this.query = searchInput.value.trim(); this.page = 1; this.loadAccounts(); }); } if (searchInput) { searchInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { this.query = searchInput.value.trim(); this.page = 1; this.loadAccounts(); } }); } // 分页 const prevBtn = document.getElementById('prevPageBtn'); const nextBtn = document.getElementById('nextPageBtn'); if (prevBtn) { prevBtn.addEventListener('click', () => { if (this.page > 1) { this.page -= 1; this.loadAccounts(); } }); } if (nextBtn) { nextBtn.addEventListener('click', () => { const maxPage = Math.max(1, Math.ceil(this.total / this.pageSize)); if (this.page < maxPage) { this.page += 1; this.loadAccounts(); } }); } // 导入相关(存在才绑定) const executeImportBtn = document.getElementById('executeImportBtn'); if (executeImportBtn) { executeImportBtn.addEventListener('click', this.executeImport.bind(this)); } const clearImportBtn = document.getElementById('clearImportBtn'); if (clearImportBtn) { clearImportBtn.addEventListener('click', this.clearImport.bind(this)); } // 导出 document.getElementById('exportDataBtn').addEventListener('click', this.exportData.bind(this)); // 退出登录 document.getElementById('logoutBtn').addEventListener('click', this.logout.bind(this)); // 单页:不使用tabs,直接加载列表 setTimeout(() => this.loadAccounts(), 100); // 打开导入对话框 const openImportBtn = document.getElementById('openImportModalBtn'); if (openImportBtn) { openImportBtn.addEventListener('click', () => { const modal = new bootstrap.Modal(document.getElementById('importModal')); modal.show(); }); } } checkStoredAuth() { // 检查是否有保存的认证信息(仅在当前会话有效) const storedToken = sessionStorage.getItem('admin_token'); if (storedToken) { this.token = storedToken; this.showManagement(); } } async handleLogin(event) { event.preventDefault(); const tokenInput = document.getElementById('tokenInput'); const enteredToken = tokenInput.value.trim(); if (!enteredToken) { this.showError('请输入管理令牌'); return; } try { // 验证令牌 const isValid = await this.verifyToken(enteredToken); if (isValid) { this.token = enteredToken; this.isAuthenticated = true; // 保存到会话存储 sessionStorage.setItem('admin_token', enteredToken); this.showManagement(); this.showSuccess('登录成功'); } else { this.showError('令牌验证失败,请检查输入的令牌是否正确'); } } catch (error) { console.error('登录失败:', error); this.showError('登录失败: ' + error.message); } } async verifyToken(token) { try { // 发送验证请求到后端 const response = await fetch('/api/admin/verify', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ token: token }) }); if (response.ok) { const result = await response.json(); return result.success; } return false; } catch (error) { console.error('令牌验证错误:', error); return false; } } showManagement() { document.getElementById('loginSection').style.display = 'none'; document.getElementById('managementSection').style.display = 'block'; // 自动加载账号列表 this.loadAccounts(); } async loadAccounts() { const accountsList = document.getElementById('accountsList'); // 显示加载状态(表格内) const tbodyLoading = document.getElementById('accountsTbody'); if (tbodyLoading) { tbodyLoading.innerHTML = `
正在加载账号列表...
`; } try { const params = new URLSearchParams(); params.set('page', String(this.page)); params.set('page_size', String(this.pageSize)); if (this.query) params.set('q', this.query); const response = await fetch(`/api/accounts/paged?${params.toString()}`, { headers: { 'Authorization': `Bearer ${this.token}` } }); if (response.ok) { const result = await response.json(); if (result.success) { const items = (result.data && result.data.items) ? result.data.items : []; this.total = (result.data && typeof result.data.total === 'number') ? result.data.total : items.length; this.page = (result.data && typeof result.data.page === 'number') ? result.data.page : this.page; this.pageSize = (result.data && typeof result.data.page_size === 'number') ? result.data.page_size : this.pageSize; this.renderAccounts(items); this.renderPager(); } else { throw new Error(result.message); } } else { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } } catch (error) { console.error('加载账号列表失败:', error); accountsList.innerHTML = `
加载失败: ${error.message}
`; } } async renderAccounts(accounts) { const tbody = document.getElementById('accountsTbody'); if (!tbody) return; if (accounts.length === 0) { tbody.innerHTML = `
暂无账号数据
`; return; } // 获取所有账户的标签信息 const accountsWithTags = await this.loadAccountsWithTags(); const accountsHtml = accounts.map((account, index) => { const tags = accountsWithTags[account.email] || []; const tagsHtml = tags.length > 0 ? tags.map(tag => `${this.escapeHtml(tag)}`).join('') : '无标签'; return ` ${account.email} ${tagsHtml}
`; }).join(''); tbody.innerHTML = accountsHtml; } // 已移除测试与系统配置相关功能 // 删除账户功能不在精简范围内,已移除调用 clearImport() { document.getElementById('importTextarea').value = ''; document.getElementById('mergeMode').value = 'update'; this.showSuccess('已清空导入内容'); } async executeImport() { const textarea = document.getElementById('importTextarea'); const mergeMode = document.getElementById('mergeMode'); const importText = textarea.value.trim(); if (!importText) { this.showError('请输入要导入的账户数据'); return; } try { // 解析文本 const parseResponse = await fetch('/api/parse-import-text', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ text: importText }) }); if (!parseResponse.ok) { throw new Error(`解析失败: HTTP ${parseResponse.status}`); } const parseResult = await parseResponse.json(); if (!parseResult.success) { throw new Error(parseResult.message); } // 执行导入 const importResponse = await fetch('/api/import', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ accounts: parseResult.data.accounts, // 只提取accounts数组 merge_mode: mergeMode.value }) }); if (!importResponse.ok) { throw new Error(`导入失败: HTTP ${importResponse.status}`); } const importResult = await importResponse.json(); if (importResult.success) { this.showSuccess(`导入完成! 新增: ${importResult.added_count}, 更新: ${importResult.updated_count}, 跳过: ${importResult.skipped_count}`); // 清空导入内容 document.getElementById('importTextarea').value = ''; this.loadAccounts(); // 刷新账号列表 } else { this.showError(`导入失败: ${importResult.message}`); } } catch (error) { console.error('导入失败:', error); this.showError(`导入失败: ${error.message}`); } } async exportData() { try { const response = await fetch('/api/export'); if (response.ok) { // 直接获取文本内容 const content = await response.text(); // 从响应头获取文件名 const contentDisposition = response.headers.get('Content-Disposition'); let filename = 'outlook_accounts_config.txt'; if (contentDisposition) { const match = contentDisposition.match(/filename=(.+)/); if (match) { filename = match[1]; } } // 下载文件 this.downloadTextFile(content, filename); this.showSuccess('数据导出成功'); } else { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } } catch (error) { console.error('导出失败:', error); this.showError(`导出失败: ${error.message}`); } } downloadTextFile(content, filename) { const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } logout() { this.isAuthenticated = false; this.token = ''; sessionStorage.removeItem('admin_token'); document.getElementById('managementSection').style.display = 'none'; document.getElementById('loginSection').style.display = 'block'; // 清空表单 document.getElementById('tokenInput').value = ''; this.showSuccess('已安全退出管理'); } showSuccess(message) { document.getElementById('successMessage').textContent = message; const modal = new bootstrap.Modal(document.getElementById('successModal')); modal.show(); } showError(message) { document.getElementById('errorMessage').textContent = message; const modal = new bootstrap.Modal(document.getElementById('errorModal')); modal.show(); } // ==================== 标签管理功能 ==================== async loadAccountsWithTags() { try { const response = await fetch('/api/accounts/tags', { headers: { 'Authorization': `Bearer ${this.token}` } }); if (response.ok) { const result = await response.json(); if (result.success) { return result.data.accounts || {}; } } } catch (error) { console.error('加载账户标签失败:', error); } return {}; } async showTagManagementDialog(email) { // 显示标签管理对话框 const modal = new bootstrap.Modal(document.getElementById('tagManagementModal')); const modalTitle = document.getElementById('tagManagementModalTitle'); const tagInput = document.getElementById('tagManagementInput'); const currentTagsDisplay = document.getElementById('tagManagementCurrentTags'); modalTitle.textContent = `管理标签 - ${email}`; // 加载当前标签 try { const response = await fetch(`/api/account/${encodeURIComponent(email)}/tags`, { headers: { 'Authorization': `Bearer ${this.token}` } }); if (response.ok) { const result = await response.json(); if (result.success) { const tags = result.data.tags || []; tagInput.value = tags.join(','); if (tags.length > 0) { const tagsHtml = tags.map(tag => `${this.escapeHtml(tag)}` ).join(''); currentTagsDisplay.innerHTML = tagsHtml; } else { currentTagsDisplay.innerHTML = '暂无标签'; } } } } catch (error) { console.error('加载账户标签失败:', error); } // 设置保存按钮事件 const saveBtn = document.getElementById('tagManagementSaveBtn'); saveBtn.onclick = () => this.saveAccountTagsFromDialog(email); modal.show(); } async saveAccountTagsFromDialog(email) { const tagInput = document.getElementById('tagManagementInput'); const tagsText = tagInput.value.trim(); const tags = tagsText ? tagsText.split(',').map(tag => tag.trim()).filter(tag => tag) : []; try { const response = await fetch(`/api/account/${encodeURIComponent(email)}/tags`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ email: email, tags: tags }) }); if (response.ok) { const result = await response.json(); if (result.success) { this.showSuccess('标签保存成功'); // 关闭对话框 const modal = bootstrap.Modal.getInstance(document.getElementById('tagManagementModal')); modal.hide(); // 刷新账户列表 this.loadAccounts(); } else { this.showError('保存失败: ' + result.message); } } else { this.showError(`保存失败: HTTP ${response.status}`); } } catch (error) { console.error('保存标签失败:', error); this.showError('保存标签失败: ' + error.message); } } async loadAllTags() { const allTagsList = document.getElementById('allTagsList'); allTagsList.innerHTML = `
加载中...
`; try { const response = await fetch('/api/accounts/tags', { headers: { 'Authorization': `Bearer ${this.token}` } }); if (response.ok) { const result = await response.json(); if (result.success) { this.renderAllTags(result.data.tags || []); } else { throw new Error(result.message); } } else { throw new Error(`HTTP ${response.status}`); } } catch (error) { console.error('加载标签失败:', error); allTagsList.innerHTML = `
加载失败: ${error.message}
`; } } renderAllTags(tags) { const allTagsList = document.getElementById('allTagsList'); if (tags.length === 0) { allTagsList.innerHTML = `
暂无标签
`; return; } const tagsHtml = tags.map(tag => ` ${this.escapeHtml(tag)} `).join(''); allTagsList.innerHTML = `
共 ${tags.length} 个标签:
${tagsHtml}
`; } async loadAccountsForTags() { const tagAccountSelect = document.getElementById('tagAccountSelect'); try { const response = await fetch('/api/accounts'); if (response.ok) { const result = await response.json(); if (result.success) { tagAccountSelect.innerHTML = ''; result.data.forEach(account => { const option = document.createElement('option'); option.value = account.email; option.textContent = account.email; tagAccountSelect.appendChild(option); }); } } } catch (error) { console.error('加载账户列表失败:', error); } } async loadAccountTags() { const tagAccountSelect = document.getElementById('tagAccountSelect'); const accountTagsInput = document.getElementById('accountTagsInput'); const currentAccountTags = document.getElementById('currentAccountTags'); const currentTagsDisplay = document.getElementById('currentTagsDisplay'); const selectedEmail = tagAccountSelect.value; if (!selectedEmail) { accountTagsInput.value = ''; currentAccountTags.style.display = 'none'; return; } try { const response = await fetch(`/api/account/${encodeURIComponent(selectedEmail)}/tags`, { headers: { 'Authorization': `Bearer ${this.token}` } }); if (response.ok) { const result = await response.json(); if (result.success) { const tags = result.data.tags || []; accountTagsInput.value = tags.join(','); if (tags.length > 0) { const tagsHtml = tags.map(tag => `${this.escapeHtml(tag)}` ).join(''); currentTagsDisplay.innerHTML = tagsHtml; currentAccountTags.style.display = 'block'; } else { currentAccountTags.style.display = 'none'; } } } } catch (error) { console.error('加载账户标签失败:', error); this.showError('加载账户标签失败: ' + error.message); } } async saveAccountTags() { const tagAccountSelect = document.getElementById('tagAccountSelect'); const accountTagsInput = document.getElementById('accountTagsInput'); const selectedEmail = tagAccountSelect.value; if (!selectedEmail) { this.showError('请先选择账户'); return; } const tagsText = accountTagsInput.value.trim(); const tags = tagsText ? tagsText.split(',').map(tag => tag.trim()).filter(tag => tag) : []; try { const response = await fetch(`/api/account/${encodeURIComponent(selectedEmail)}/tags`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ email: selectedEmail, tags: tags }) }); if (response.ok) { const result = await response.json(); if (result.success) { this.showSuccess('标签保存成功'); this.loadAccountTags(); // 刷新显示 this.loadAllTags(); // 刷新所有标签列表 } else { this.showError('保存失败: ' + result.message); } } else { this.showError(`保存失败: HTTP ${response.status}`); } } catch (error) { console.error('保存标签失败:', error); this.showError('保存标签失败: ' + error.message); } } clearAccountTags() { document.getElementById('accountTagsInput').value = ''; document.getElementById('currentAccountTags').style.display = 'none'; } // ==================== 分页信息渲染 ==================== renderPager() { const info = document.getElementById('accountsPagerInfo'); if (!info) return; const maxPage = Math.max(1, Math.ceil(this.total / this.pageSize)); info.textContent = `第 ${this.page} / ${maxPage} 页,共 ${this.total} 条`; const prevBtn = document.getElementById('prevPageBtn'); const nextBtn = document.getElementById('nextPageBtn'); if (prevBtn) prevBtn.disabled = this.page <= 1; if (nextBtn) nextBtn.disabled = this.page >= maxPage; } // ==================== 辅助方法 ==================== escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } formatDate(dateString) { if (!dateString) return '未知时间'; try { const date = new Date(dateString); if (isNaN(date.getTime())) { return dateString; } const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); const timeDiff = today.getTime() - messageDate.getTime(); const daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24)); if (daysDiff === 0) { return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } else if (daysDiff === 1) { return '昨天'; } else if (daysDiff < 7) { return `${daysDiff}天前`; } else { return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }); } } catch (error) { console.error('Date formatting error:', error); return dateString; } } } // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { window.adminManager = new AdminManager(); });