const state = { channels: [], accounts: [], selectedAccountId: null, messages: [], selectedMessageUid: null, authenticated: false, }; const appShell = document.getElementById('app-shell'); const loginScreen = document.getElementById('login-screen'); const loginForm = document.getElementById('login-form'); const loginBtn = document.getElementById('login-btn'); const logoutBtn = document.getElementById('logout-btn'); const channelTree = document.getElementById('channel-tree'); const channelForm = document.getElementById('channel-form'); const accountForm = document.getElementById('account-form'); const accountSubmitBtn = document.getElementById('account-submit-btn'); const sendForm = document.getElementById('send-form'); const sendBtn = document.getElementById('send-btn'); const refreshMailsBtn = document.getElementById('refresh-mails-btn'); const loadMailsBtn = document.getElementById('load-mails-btn'); const selectedAccountTitle = document.getElementById('selected-account-title'); const selectedAccountMeta = document.getElementById('selected-account-meta'); const messageList = document.getElementById('message-list'); const messageDetail = document.getElementById('message-detail'); const toast = document.getElementById('toast'); const composeModal = document.getElementById('compose-modal'); const settingsModal = document.getElementById('settings-modal'); const composeBtn = document.getElementById('compose-btn'); const settingsBtn = document.getElementById('settings-btn'); const closeCompose = document.getElementById('close-compose'); const closeSettings = document.getElementById('close-settings'); const batchImportForm = document.getElementById('batch-import-form'); const batchImportContent = document.getElementById('batch-import-content'); const batchImportBtn = document.getElementById('batch-import-btn'); const exportAccountsBtn = document.getElementById('export-accounts-btn'); const copyExportBtn = document.getElementById('copy-export-btn'); boot(); async function boot() { bindEvents(); await restoreSession(); } function bindEvents() { loginForm.addEventListener('submit', onLogin); logoutBtn.addEventListener('click', onLogout); composeBtn.addEventListener('click', () => openModal(composeModal)); settingsBtn.addEventListener('click', () => openModal(settingsModal)); closeCompose.addEventListener('click', () => closeModal(composeModal)); closeSettings.addEventListener('click', () => closeModal(settingsModal)); composeModal.querySelector('.modal-backdrop').addEventListener('click', () => closeModal(composeModal)); settingsModal.querySelector('.modal-backdrop').addEventListener('click', () => closeModal(settingsModal)); channelForm.addEventListener('submit', onCreateChannel); accountForm.addEventListener('submit', onCreateAccount); batchImportForm.addEventListener('submit', onBatchImport); sendForm.addEventListener('submit', onSendMail); refreshMailsBtn.addEventListener('click', () => loadMessages(true)); loadMailsBtn.addEventListener('click', () => loadMessages(false)); exportAccountsBtn.addEventListener('click', onExportAccounts); copyExportBtn.addEventListener('click', onCopyExportAccounts); } async function restoreSession() { const result = await request('/api/auth/status', {}, false); state.authenticated = Boolean(result.authenticated); updateAuthView(); if (state.authenticated) { await reloadBaseData(); } } function updateAuthView() { loginScreen.classList.toggle('hidden', state.authenticated); appShell.classList.toggle('hidden', !state.authenticated); } async function onLogin(event) { event.preventDefault(); const payload = Object.fromEntries(new FormData(loginForm).entries()); loginBtn.disabled = true; loginBtn.textContent = '验证中...'; try { await request('/api/auth/login', { method: 'POST', body: JSON.stringify(payload), }, false); state.authenticated = true; updateAuthView(); loginForm.reset(); await reloadBaseData(); showToast('登录成功'); } finally { loginBtn.disabled = false; loginBtn.textContent = '进入系统'; } } async function onLogout() { await request('/api/auth/logout', { method: 'POST' }, false); state.authenticated = false; state.selectedAccountId = null; state.selectedMessageUid = null; state.messages = []; syncSelectionState(); renderChannelTree(); renderMessages(); updateAuthView(); showToast('已退出登录'); } function openModal(modal) { modal.classList.remove('hidden'); } function closeModal(modal) { modal.classList.add('hidden'); } async function reloadBaseData() { const [channels, accounts] = await Promise.all([ request('/api/channels'), request('/api/accounts'), ]); state.channels = channels; state.accounts = accounts; renderAccountOptions(); renderChannelTree(); } async function onCreateChannel(event) { event.preventDefault(); const formData = new FormData(channelForm); const payload = Object.fromEntries(formData.entries()); payload.imap_secure = formData.get('imap_secure') === 'on'; payload.smtp_secure = formData.get('smtp_secure') === 'on'; await request('/api/channels', { method: 'POST', body: JSON.stringify(payload), }); channelForm.reset(); channelForm.querySelector('[name="imap_secure"]').checked = true; channelForm.querySelector('[name="smtp_secure"]').checked = true; showToast('渠道已创建'); await reloadBaseData(); } async function onCreateAccount(event) { event.preventDefault(); const payload = Object.fromEntries(new FormData(accountForm).entries()); accountSubmitBtn.disabled = true; accountSubmitBtn.textContent = '保存中...'; try { await request('/api/accounts', { method: 'POST', body: JSON.stringify(payload), }); accountForm.reset(); showToast('邮箱帐号已导入'); await reloadBaseData(); } finally { accountSubmitBtn.disabled = false; accountSubmitBtn.textContent = '导入帐号'; } } async function onSendMail(event) { event.preventDefault(); if (!state.selectedAccountId) { showToast('请先选择邮箱帐号', true); return; } const payload = Object.fromEntries(new FormData(sendForm).entries()); sendBtn.disabled = true; sendBtn.textContent = '发送中...'; try { await request(`/api/accounts/${state.selectedAccountId}/messages/send`, { method: 'POST', body: JSON.stringify(payload), }); sendForm.reset(); closeModal(composeModal); showToast('邮件发送成功'); } finally { sendBtn.disabled = false; sendBtn.textContent = '发送'; } } async function onBatchImport(event) { event.preventDefault(); const content = batchImportContent.value.trim(); if (!content) { showToast('请先粘贴导入内容', true); return; } batchImportBtn.disabled = true; batchImportBtn.textContent = '导入中...'; try { const result = await request('/api/accounts/import', { method: 'POST', body: JSON.stringify({ content }), }); await reloadBaseData(); showToast(`导入完成,新增 ${result.created} 条,更新 ${result.updated} 条`); } finally { batchImportBtn.disabled = false; batchImportBtn.textContent = '批量导入'; } } async function onExportAccounts() { const result = await request('/api/accounts/export'); batchImportContent.value = result.content || ''; showToast('导出内容已填入文本框'); } async function onCopyExportAccounts() { const content = batchImportContent.value.trim(); if (!content) { const result = await request('/api/accounts/export'); batchImportContent.value = result.content || ''; } await copyText(batchImportContent.value); showToast('导出内容已复制'); } function renderAccountOptions() { const select = accountForm.querySelector('select[name="channel_id"]'); select.innerHTML = '' + state.channels .map((channel) => ``) .join(''); } function renderChannelTree() { if (!state.channels.length) { channelTree.innerHTML = '
暂无邮件,点击刷新收取
选择一封邮件查看详情