// 全局错误处理,防止JavaScript错误中断执行 window.onerror = function(msg, url, line, col, error) { console.error('JavaScript错误:', msg, 'at', url, 'line:', line); return true; // 阻止错误传播 }; // ============== Toast 消息提示系统 ============== // 非阻塞消息提示,替代 alert() 弹窗 // 用法: showToast('消息内容', 'success'); // type: success/error/warning/info // showToast('消息内容'); // 默认 auto 自动判断类型 const TOAST_ICONS = { success: '✓', error: '✕', warning: '!', info: 'i' }; let toastContainer = null; function getToastContainer() { if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.className = 'toast-container'; document.body.appendChild(toastContainer); } return toastContainer; } /** * 显示消息提示(非阻塞) * @param {string} message - 消息内容 * @param {string} type - 类型: success/error/warning/info/auto * @param {number} duration - 显示时长(毫秒),默认 3000ms,error 默认 5000ms */ function showToast(message, type = 'auto', duration = null) { // 自动判断类型 if (type === 'auto') { const msg = String(message); if (/成功|完成|已保存|已删除|已更新|已启用|已禁用|已复制|已导入|已导出|已采集/.test(msg)) { type = 'success'; } else if (/失败|错误|异常|无法|不能|出错/.test(msg)) { type = 'error'; } else if (/请|警告|注意|必须|需要/.test(msg)) { type = 'warning'; } else { type = 'info'; } } // 默认时长:error 5000ms,其他 3000ms if (duration === null) { duration = type === 'error' ? 5000 : 3000; } const container = getToastContainer(); const toast = document.createElement('div'); toast.className = `toast toast-${type}`; const icon = TOAST_ICONS[type] || ''; toast.innerHTML = ` ${icon} `; // 安全设置消息内容(防 XSS) toast.querySelector('.toast-content').textContent = String(message); // 关闭按钮 toast.querySelector('.toast-close').addEventListener('click', function() { removeToast(toast); }); container.appendChild(toast); // 自动消失 if (duration > 0) { setTimeout(function() { removeToast(toast); }, duration); } return toast; } function removeToast(toast) { if (!toast || !toast.parentNode) return; toast.classList.add('toast-fade-out'); setTimeout(function() { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 300); } // 便捷方法 function showSuccess(message, duration) { return showToast(message, 'success', duration); } function showError(message, duration) { return showToast(message, 'error', duration); } function showWarning(message, duration) { return showToast(message, 'warning', duration); } function showInfo(message, duration) { return showToast(message, 'info', duration); } // 全局变量 let editingConfigId = null; let allFields = []; let currentPage = 1; let pageSize = 10; let currentEntityType = ''; let currentSearch = ''; let entityPage = 1; let entityPageSize = 20; let selectedEntityIds = new Set(); let fieldCustomNames = {}; // ============== 登录相关 ============== const AUTH_TOKEN_KEY = 'daily_report_auth_token'; const AUTH_USER_KEY = 'daily_report_auth_user'; // 获取token function getToken() { return localStorage.getItem(AUTH_TOKEN_KEY) || ''; } // 保存登录状态 function setAuth(token, user) { localStorage.setItem(AUTH_TOKEN_KEY, token); localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user)); } // 清除登录状态 function clearAuth() { localStorage.removeItem(AUTH_TOKEN_KEY); localStorage.removeItem(AUTH_USER_KEY); } // 检查是否已登录 function isLoggedIn() { return !!getToken(); } // 获取当前用户 function getCurrentUser() { const userStr = localStorage.getItem(AUTH_USER_KEY); return userStr ? JSON.parse(userStr) : null; } // 全局拦截 fetch,自动加 token const originalFetch = window.fetch; window.fetch = function(url, options = {}) { const token = getToken(); if (token) { options.headers = options.headers || {}; options.headers['Authorization'] = 'Bearer ' + token; } return originalFetch(url, options).then(response => { if (response.status === 401) { clearAuth(); showLoginPage(); } return response; }); }; // 显示登录页 function showLoginPage() { document.getElementById('login-overlay').style.display = 'flex'; document.getElementById('main-container').style.display = 'none'; document.getElementById('login-password').value = ''; document.getElementById('login-password').focus(); } // 隐藏登录页 function hideLoginPage() { document.getElementById('login-overlay').style.display = 'none'; document.getElementById('main-container').style.display = 'block'; const user = getCurrentUser(); if (user) { document.getElementById('user-display').textContent = user.real_name || user.username; } } // 登录 async function handleLogin(event) { event.preventDefault(); const username = document.getElementById('login-username').value.trim(); const password = document.getElementById('login-password').value.trim(); if (!username || !password) { showToast('请输入用户名和密码'); return; } const btn = document.getElementById('login-btn'); btn.disabled = true; btn.textContent = '登录中...'; try { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const result = await response.json(); if (result.success) { setAuth(result.data.token, { username: result.data.username, real_name: result.data.real_name }); hideLoginPage(); // 登录成功后加载数据 loadConfigs(); } else { showToast(result.message || '登录失败'); } } catch (error) { showToast('登录失败: ' + error.message); } finally { btn.disabled = false; btn.textContent = '登 录'; } } // 退出登录 async function handleLogout() { if (!confirm('确定要退出登录吗?')) return; try { await fetch('/api/auth/logout', { method: 'POST' }); } catch (e) { // 忽略错误 } clearAuth(); showLoginPage(); } // 修改密码弹窗 function showChangePasswordModal() { document.getElementById('old-password').value = ''; document.getElementById('new-password').value = ''; document.getElementById('confirm-password').value = ''; document.getElementById('change-password-modal').style.display = 'block'; } function closeChangePasswordModal() { document.getElementById('change-password-modal').style.display = 'none'; } async function saveChangePassword() { const oldPassword = document.getElementById('old-password').value.trim(); const newPassword = document.getElementById('new-password').value.trim(); const confirmPassword = document.getElementById('confirm-password').value.trim(); if (!oldPassword || !newPassword || !confirmPassword) { showToast('请填写完整信息'); return; } if (newPassword.length < 6) { showToast('新密码长度不能少于6位'); return; } if (newPassword !== confirmPassword) { showToast('两次输入的新密码不一致'); return; } try { const response = await fetch('/api/auth/change-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ old_password: oldPassword, new_password: newPassword }) }); const result = await response.json(); if (result.success) { showToast('密码修改成功,请重新登录'); closeChangePasswordModal(); clearAuth(); showLoginPage(); } else { showToast(result.message || '修改失败'); } } catch (error) { showToast('修改失败: ' + error.message); } } // 页面加载时检查登录状态 document.addEventListener('DOMContentLoaded', function() { if (isLoggedIn()) { hideLoginPage(); } else { showLoginPage(); } }); // 标签切换 function switchTab(tabName) { document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); document.querySelector(`.tab[onclick="switchTab('${tabName}')"]`).classList.add('active'); document.getElementById(`${tabName}-tab`).classList.add('active'); if (tabName === 'config') { loadConfigs(); } else if (tabName === 'generate') { loadGenerateConfigs(); } else if (tabName === 'history') { loadHistory(); } else if (tabName === 'sum-data') { loadSumData(); } } // 格式化日期时间 function formatDateTime(dateStr) { if (!dateStr) return ''; const date = new Date(dateStr); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } // 页面加载时自动加载配置列表 document.addEventListener('DOMContentLoaded', function() { loadConfigs(); });