/** * 共享UI组件库 */ // 显示通知消息 function showNotification(message, type = 'success', duration = 3000) { const notificationContainer = document.getElementById('notification-container') || createNotificationContainer(); const notification = document.createElement('div'); notification.className = `notification notification-${type} flex items-center p-4 mb-3 rounded-md shadow-md transform transition-all duration-300 ease-in-out translate-x-full`; // 设置图标 let icon = ''; switch(type) { case 'success': icon = ''; break; case 'warning': icon = ''; break; case 'error': icon = ''; break; default: icon = ''; } notification.innerHTML = ` ${icon} ${message} `; notificationContainer.appendChild(notification); // 关闭按钮功能 notification.querySelector('button').addEventListener('click', () => { closeNotification(notification); }); // 显示动画 setTimeout(() => { notification.classList.remove('translate-x-full'); notification.classList.add('translate-x-0'); }, 10); // 自动关闭 if (duration) { setTimeout(() => { closeNotification(notification); }, duration); } return notification; } function closeNotification(notification) { notification.classList.remove('translate-x-0'); notification.classList.add('translate-x-full'); setTimeout(() => { notification.remove(); }, 300); } function createNotificationContainer() { const container = document.createElement('div'); container.id = 'notification-container'; container.className = 'fixed top-4 right-4 z-50 w-80 flex flex-col items-end'; document.body.appendChild(container); return container; } // 确认对话框 function confirmDialog(message, options = {}) { return new Promise((resolve) => { const defaults = { title: '确认操作', confirmText: '确认', cancelText: '取消', type: 'question' // question, warning, danger }; const settings = {...defaults, ...options}; // 创建遮罩 const overlay = document.createElement('div'); overlay.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 transition-opacity duration-300 opacity-0'; overlay.style.backdropFilter = 'blur(2px)'; // 选择颜色和图标 let iconClass = 'text-indigo-500'; let buttonClass = 'bg-indigo-600 hover:bg-indigo-700'; let icon = ''; if (settings.type === 'warning') { iconClass = 'text-amber-500'; buttonClass = 'bg-amber-600 hover:bg-amber-700'; icon = ''; } else if (settings.type === 'danger') { iconClass = 'text-red-500'; buttonClass = 'bg-red-600 hover:bg-red-700'; icon = ''; } // 创建对话框 overlay.innerHTML = `
${icon}

${settings.title}

${message}

`; document.body.appendChild(overlay); // 显示动画 setTimeout(() => { overlay.classList.remove('opacity-0'); overlay.classList.add('opacity-100'); const dialog = overlay.querySelector('div.bg-white'); dialog.classList.remove('scale-95', 'opacity-0'); dialog.classList.add('scale-100', 'opacity-100'); }, 10); // 事件处理 const closeDialog = (result) => { // 关闭动画 overlay.classList.remove('opacity-100'); overlay.classList.add('opacity-0'); const dialog = overlay.querySelector('div.bg-white'); dialog.classList.remove('scale-100', 'opacity-100'); dialog.classList.add('scale-95', 'opacity-0'); // 等待动画完成后移除 setTimeout(() => { overlay.remove(); resolve(result); }, 300); }; // 处理键盘事件 const handleKeyDown = (e) => { if (e.key === 'Escape') { closeDialog(false); document.removeEventListener('keydown', handleKeyDown); } else if (e.key === 'Enter') { closeDialog(true); document.removeEventListener('keydown', handleKeyDown); } }; document.addEventListener('keydown', handleKeyDown); // 绑定按钮事件 overlay.querySelector('#confirm-btn').addEventListener('click', () => { closeDialog(true); document.removeEventListener('keydown', handleKeyDown); }); overlay.querySelector('#cancel-btn').addEventListener('click', () => { closeDialog(false); document.removeEventListener('keydown', handleKeyDown); }); // 点击外部区域关闭 overlay.addEventListener('click', (e) => { if (e.target === overlay) { closeDialog(false); document.removeEventListener('keydown', handleKeyDown); } }); }); } // 表格排序 function initTableSort() { document.querySelectorAll('table.sortable').forEach(table => { table.querySelectorAll('th.sortable').forEach(header => { header.addEventListener('click', () => { const index = Array.from(header.parentNode.children).indexOf(header); const isNumeric = header.classList.contains('sort-numeric'); const isDate = header.classList.contains('sort-date'); const isAsc = !header.classList.contains('sort-asc'); // 清除所有表头的排序状态 table.querySelectorAll('th.sortable').forEach(th => { th.classList.remove('sort-asc', 'sort-desc'); }); // 设置当前表头的排序状态 header.classList.add(isAsc ? 'sort-asc' : 'sort-desc'); // 获取并排序行 const tbody = table.querySelector('tbody'); const rows = Array.from(tbody.querySelectorAll('tr')); rows.sort((a, b) => { let aValue = a.children[index].textContent.trim(); let bValue = b.children[index].textContent.trim(); if (isNumeric) { aValue = parseFloat(aValue); bValue = parseFloat(bValue); return isAsc ? aValue - bValue : bValue - aValue; } else if (isDate) { aValue = new Date(aValue); bValue = new Date(bValue); return isAsc ? aValue - bValue : bValue - aValue; } else { return isAsc ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); } }); // 移除现有行并按排序顺序重新添加 rows.forEach(row => tbody.appendChild(row)); }); }); }); } // 初始化时绑定组件行为 document.addEventListener('DOMContentLoaded', function() { // 初始化排序表格 initTableSort(); // 绑定确认删除按钮 document.querySelectorAll('[data-confirm]').forEach(button => { button.addEventListener('click', async (e) => { e.preventDefault(); const message = button.dataset.confirm || '确定要执行此操作吗?'; const type = button.dataset.confirmType || 'warning'; const confirmed = await confirmDialog(message, { type }); if (confirmed) { if (button.tagName === 'A') { window.location.href = button.href; } else if (button.form) { button.form.submit(); } } }); }); });