/** * 全局应用函数 */ // 核心工具函数 const App = { /** * 初始化应用 */ init() { this.setupEventListeners(); this.setupTheme(); }, /** * 设置事件监听器 */ setupEventListeners() { // 全局事件委托 document.addEventListener('click', (e) => { // 处理移动导航菜单切换 if (e.target.matches('#mobile-menu-toggle') || e.target.closest('#mobile-menu-toggle')) { this.toggleMobileMenu(); } // 处理通知关闭按钮 if (e.target.matches('.close-notification') || e.target.closest('.close-notification')) { const notification = e.target.closest('.notification'); if (notification) this.closeNotification(notification); } // 处理模态框关闭 if (e.target.matches('.modal-overlay')) { const modalId = e.target.dataset.modalId; if (modalId) this.closeModal(modalId); } }); // 响应键盘事件 document.addEventListener('keydown', (e) => { // ESC键关闭模态框 if (e.key === 'Escape') { const modal = document.querySelector('.modal.is-active'); if (modal) { const modalId = modal.id; this.closeModal(modalId); } } }); }, /** * 设置主题相关功能 */ setupTheme() { // 读取用户偏好 const savedTheme = localStorage.getItem('theme'); if (savedTheme) { document.documentElement.setAttribute('data-theme', savedTheme); } // 监听主题切换按钮 document.querySelectorAll('[data-toggle-theme]').forEach(btn => { btn.addEventListener('click', () => { const currentTheme = document.documentElement.getAttribute('data-theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); }); }); }, /** * 切换移动菜单 */ toggleMobileMenu() { const mobileMenu = document.getElementById('mobile-menu'); if (mobileMenu) { const isActive = mobileMenu.classList.contains('active'); if (isActive) { mobileMenu.classList.remove('active'); mobileMenu.classList.add('inactive'); } else { mobileMenu.classList.remove('inactive'); mobileMenu.classList.add('active'); } } }, /** * 显示通知 * @param {string} message - 通知消息 * @param {string} type - 通知类型 (success, error, warning, info) * @param {number} duration - 显示时长(ms),0为不自动关闭 */ notify(message, type = 'info', duration = 5000) { // 如果已存在通知容器则使用,否则创建 let container = document.getElementById('notification-container'); if (!container) { container = document.createElement('div'); container.id = 'notification-container'; container.className = 'fixed top-4 right-4 z-50 flex flex-col items-end space-y-2'; document.body.appendChild(container); } // 创建通知元素 const id = 'notification-' + Date.now(); const notification = document.createElement('div'); // 设置通知样式 notification.id = id; notification.className = `notification bg-white shadow-md rounded-md p-4 transform translate-x-full transition-transform duration-300 max-w-sm flex items-start border-l-4 ${this._getNotificationColorClass(type)}`; // 设置通知内容 notification.innerHTML = `

${message}

`; // 添加到容器 container.appendChild(notification); // 激活动画 setTimeout(() => { notification.classList.remove('translate-x-full'); notification.classList.add('translate-x-0'); }, 10); // 设置自动关闭 if (duration > 0) { setTimeout(() => { this.closeNotification(notification); }, duration); } return id; }, /** * 关闭通知 * @param {HTMLElement|string} notification - 通知元素或ID */ closeNotification(notification) { // 如果传入字符串ID,则获取对应元素 if (typeof notification === 'string') { notification = document.getElementById(notification); if (!notification) return; } // 动画关闭 notification.classList.remove('translate-x-0'); notification.classList.add('translate-x-full'); // 移除元素 setTimeout(() => { if (notification && notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, /** * 获取通知颜色类名 * @private */ _getNotificationColorClass(type) { switch (type) { case 'success': return 'border-green-500'; case 'error': return 'border-red-500'; case 'warning': return 'border-yellow-500'; default: return 'border-blue-500'; } }, /** * 获取通知图标类名 * @private */ _getNotificationIconClass(type) { const baseClass = 'text-white rounded-full p-1 flex items-center justify-center'; switch (type) { case 'success': return `${baseClass} bg-green-500`; case 'error': return `${baseClass} bg-red-500`; case 'warning': return `${baseClass} bg-yellow-500`; default: return `${baseClass} bg-blue-500`; } }, /** * 打开模态框 * @param {string} id - 模态框ID */ openModal(id) { const modal = document.getElementById(id); if (!modal) return; modal.classList.add('is-active'); document.body.classList.add('modal-open'); // 创建动画效果 modal.querySelector('.modal-content').classList.add('scale-100', 'opacity-100'); modal.querySelector('.modal-content').classList.remove('scale-95', 'opacity-0'); }, /** * 关闭模态框 * @param {string} id - 模态框ID */ closeModal(id) { const modal = document.getElementById(id); if (!modal) return; const content = modal.querySelector('.modal-content'); content.classList.remove('scale-100', 'opacity-100'); content.classList.add('scale-95', 'opacity-0'); // 等待动画完成后隐藏 setTimeout(() => { modal.classList.remove('is-active'); document.body.classList.remove('modal-open'); }, 200); } }; // 初始化 document.addEventListener('DOMContentLoaded', () => { App.init(); });