/** * YCat Global Loading Component * 全局 Loading 覆盖层 + 骨架屏 */ const Loading = { overlay: null, isShowing: false, /** * 显示全局 Loading * @param {string} text - 提示文字 * @param {boolean} showProgress - 是否显示进度条 */ show(text = '加载中...', showProgress = false) { if (this.isShowing) return; this.isShowing = true; // 创建 overlay this.overlay = document.createElement('div'); this.overlay.id = 'globalLoadingOverlay'; this.overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.92); backdrop-filter: blur(4px); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 99999; animation: fadeIn 0.2s ease; `; // 添加样式 if (!document.getElementById('loading-styles')) { const style = document.createElement('style'); style.id = 'loading-styles'; style.textContent = ` @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .loading-spinner { width: 48px; height: 48px; border: 4px solid #f0f0f0; border-top-color: #FF6B35; border-radius: 50%; animation: spin 0.8s linear infinite; } .loading-text { margin-top: 16px; font-size: 16px; color: #333; font-weight: 500; } .loading-progress { margin-top: 12px; width: 200px; height: 4px; background: #f0f0f0; border-radius: 2px; overflow: hidden; } .loading-progress-bar { height: 100%; background: linear-gradient(90deg, #FF6B35, #4ECDC4); border-radius: 2px; transition: width 0.3s ease; animation: pulse 1.5s ease infinite; } /* 骨架屏 */ .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; border-radius: 4px; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .skeleton-text { height: 16px; margin-bottom: 8px; } .skeleton-title { height: 24px; width: 60%; margin-bottom: 16px; } .skeleton-card { height: 120px; margin-bottom: 16px; border-radius: 8px; } .skeleton-avatar { width: 40px; height: 40px; border-radius: 50%; } `; document.head.appendChild(style); } // Spinner const spinner = document.createElement('div'); spinner.className = 'loading-spinner'; this.overlay.appendChild(spinner); // Text const textEl = document.createElement('div'); textEl.className = 'loading-text'; textEl.textContent = text; this.overlay.appendChild(textEl); // Progress bar (可选) if (showProgress) { const progress = document.createElement('div'); progress.className = 'loading-progress'; progress.innerHTML = '
'; this.overlay.appendChild(progress); this.progressBar = progress.firstChild; } document.body.appendChild(this.overlay); }, /** * 更新提示文字 */ updateText(text) { if (!this.overlay) return; const textEl = this.overlay.querySelector('.loading-text'); if (textEl) textEl.textContent = text; }, /** * 更新进度条 * @param {number} percent - 0-100 */ updateProgress(percent) { if (!this.progressBar) return; this.progressBar.style.width = `${Math.min(100, Math.max(0, percent))}%`; }, /** * 隐藏 Loading */ hide() { if (!this.isShowing || !this.overlay) return; this.isShowing = false; this.overlay.style.animation = 'fadeIn 0.2s ease reverse'; setTimeout(() => { if (this.overlay && this.overlay.parentNode) { this.overlay.parentNode.removeChild(this.overlay); } this.overlay = null; this.progressBar = null; }, 200); }, /** * 创建骨架屏占位符 * @param {string} type - text/title/card/avatar * @param {object} options - { width, height, className } */ skeleton(type = 'text', options = {}) { const el = document.createElement('div'); el.className = `skeleton skeleton-${type}`; const style = []; if (options.width) style.push(`width: ${options.width}`); if (options.height) style.push(`height: ${options.height}`); if (options.className) el.className += ` ${options.className}`; if (style.length) el.style.cssText = style.join('; '); return el; }, /** * 批量创建骨架屏列表 * @param {number} count - 数量 * @param {string} type - 类型 */ skeletonList(count = 3, type = 'card') { const container = document.createElement('div'); for (let i = 0; i < count; i++) { container.appendChild(this.skeleton(type)); } return container; } }; // 导出 window.Loading = Loading;