// 文件传输服务 - 前端JavaScript class FileShareApp { constructor() { this.baseURL = window.location.origin; this.init(); } init() { this.setupEventListeners(); this.setupTabSwitching(); this.setupDragAndDrop(); } // 设置事件监听器 setupEventListeners() { // 文件选择 const fileInput = document.getElementById('fileInput'); const selectBtn = document.querySelector('.select-btn'); selectBtn?.addEventListener('click', () => fileInput?.click()); fileInput?.addEventListener('change', (e) => this.handleFileSelect(e.target.files[0])); // 文本分享 const shareTextBtn = document.getElementById('shareTextBtn'); shareTextBtn?.addEventListener('click', () => this.handleTextShare()); // 下载文件 const downloadBtn = document.getElementById('downloadBtn'); const downloadCode = document.getElementById('downloadCode'); downloadBtn?.addEventListener('click', () => this.handleDownloadInfo()); downloadCode?.addEventListener('keypress', (e) => { if (e.key === 'Enter') this.handleDownloadInfo(); }); // 重置按钮 window.resetUpload = () => this.resetUploadSection(); window.resetText = () => this.resetTextSection(); // 复制功能 window.copyCode = (elementId) => this.copyToClipboard(elementId); window.copyText = (text) => this.copyTextToClipboard(text); } // 设置选项卡切换 setupTabSwitching() { const tabBtns = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); tabBtns.forEach(btn => { btn.addEventListener('click', () => { const targetTab = btn.getAttribute('data-tab'); // 更新按钮状态 tabBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); // 更新内容显示 tabContents.forEach(content => { content.classList.remove('active'); }); const targetContent = document.getElementById(`${targetTab}-tab`); if (targetContent) { targetContent.classList.add('active'); } }); }); } // 设置拖拽上传 setupDragAndDrop() { const dropZone = document.getElementById('dropZone'); if (!dropZone) return; ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, this.preventDefaults, false); }); ['dragenter', 'dragover'].forEach(eventName => { dropZone.addEventListener(eventName, () => dropZone.classList.add('dragover'), false); }); ['dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, () => dropZone.classList.remove('dragover'), false); }); dropZone.addEventListener('drop', (e) => { const files = e.dataTransfer.files; if (files.length > 0) { this.handleFileSelect(files[0]); } }, false); } preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } // 处理文件选择 async handleFileSelect(file) { if (!file) return; // 检查文件大小 const maxSize = 100 * 1024 * 1024; // 100MB if (file.size > maxSize) { this.showToast('文件太大,最大支持100MB', 'error'); return; } try { // 显示进度界面 this.showProgress(); // 创建FormData const formData = new FormData(); formData.append('file', file); // 上传文件 const response = await fetch(`${this.baseURL}/api/upload`, { method: 'POST', body: formData, onUploadProgress: (progressEvent) => { const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); this.updateProgress(percentCompleted); } }); if (!response.ok) { throw new Error(`上传失败: ${response.statusText}`); } const result = await response.json(); this.showUploadResult(result); this.showToast('文件上传成功!', 'success'); } catch (error) { console.error('Upload error:', error); this.showToast(error.message || '上传失败', 'error'); this.hideProgress(); } } // 处理文本分享 async handleTextShare() { const content = document.getElementById('textContent').value.trim(); const filename = document.getElementById('textFilename').value.trim() || 'shared_text.txt'; if (!content) { this.showToast('请输入要分享的文本内容', 'warning'); return; } try { const shareBtn = document.getElementById('shareTextBtn'); shareBtn.classList.add('loading'); shareBtn.disabled = true; const response = await fetch(`${this.baseURL}/api/share-text`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ content: content, filename: filename }) }); if (!response.ok) { throw new Error(`分享失败: ${response.statusText}`); } const result = await response.json(); this.showTextResult(result); this.showToast('文本分享成功!', 'success'); } catch (error) { console.error('Text share error:', error); this.showToast(error.message || '分享失败', 'error'); } finally { const shareBtn = document.getElementById('shareTextBtn'); shareBtn.classList.remove('loading'); shareBtn.disabled = false; } } // 处理下载信息获取 async handleDownloadInfo() { const code = document.getElementById('downloadCode').value.trim().toUpperCase(); if (!code) { this.showToast('请输入分享码', 'warning'); return; } if (code.length !== 8) { this.showToast('分享码格式错误,应为8位字符', 'warning'); return; } try { const downloadBtn = document.getElementById('downloadBtn'); downloadBtn.classList.add('loading'); downloadBtn.disabled = true; const response = await fetch(`${this.baseURL}/api/info/${code}`); if (response.status === 404) { throw new Error('分享码不存在'); } if (response.status === 410) { throw new Error('分享已过期'); } if (!response.ok) { throw new Error(`获取信息失败: ${response.statusText}`); } const fileInfo = await response.json(); this.showFileInfo(fileInfo); } catch (error) { console.error('Download info error:', error); this.showToast(error.message || '获取文件信息失败', 'error'); this.hideFileInfo(); } finally { const downloadBtn = document.getElementById('downloadBtn'); downloadBtn.classList.remove('loading'); downloadBtn.disabled = false; } } // 处理文件下载 handleFileDownload(code) { const downloadUrl = `${this.baseURL}/api/download/${code}`; // 创建隐藏的下载链接 const link = document.createElement('a'); link.href = downloadUrl; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.showToast('开始下载文件...', 'success'); } // 显示上传进度 showProgress() { const progressSection = document.getElementById('progressSection'); const uploadResult = document.getElementById('uploadResult'); const dropZone = document.getElementById('dropZone'); dropZone.style.display = 'none'; uploadResult.style.display = 'none'; progressSection.style.display = 'block'; this.updateProgress(0); } // 更新进度条 updateProgress(percent) { const progressFill = document.getElementById('progressFill'); const progressText = document.getElementById('progressText'); if (progressFill) { progressFill.style.width = `${percent}%`; } if (progressText) { progressText.textContent = `上传中... ${percent}%`; } } // 隐藏进度条 hideProgress() { const progressSection = document.getElementById('progressSection'); const dropZone = document.getElementById('dropZone'); progressSection.style.display = 'none'; dropZone.style.display = 'block'; } // 显示上传结果 showUploadResult(result) { const progressSection = document.getElementById('progressSection'); const uploadResult = document.getElementById('uploadResult'); const uploadCode = document.getElementById('uploadCode'); const uploadLink = document.getElementById('uploadLink'); const uploadExpire = document.getElementById('uploadExpire'); progressSection.style.display = 'none'; uploadResult.style.display = 'block'; uploadCode.textContent = result.code; uploadLink.textContent = `${this.baseURL}${result.download_url}`; const expireTime = new Date(result.expires_at); const now = new Date(); const diffMinutes = Math.ceil((expireTime - now) / (1000 * 60)); uploadExpire.textContent = `${diffMinutes}分钟后过期`; // 设置下载按钮 const downloadFileBtn = document.getElementById('downloadFileBtn'); if (downloadFileBtn) { downloadFileBtn.onclick = () => this.handleFileDownload(result.code); } } // 显示文本分享结果 showTextResult(result) { const textResult = document.getElementById('textResult'); const textCode = document.getElementById('textCode'); const textLink = document.getElementById('textLink'); const textExpire = document.getElementById('textExpire'); textResult.style.display = 'block'; textCode.textContent = result.code; textLink.textContent = `${this.baseURL}${result.download_url}`; const expireTime = new Date(result.expires_at); const now = new Date(); const diffMinutes = Math.ceil((expireTime - now) / (1000 * 60)); textExpire.textContent = `${diffMinutes}分钟后过期`; // 隐藏输入区域 document.querySelector('.text-input-area').style.display = 'none'; } // 显示文件信息 showFileInfo(fileInfo) { const fileInfoDiv = document.getElementById('fileInfo'); const fileName = document.getElementById('fileName'); const fileSize = document.getElementById('fileSize'); const fileType = document.getElementById('fileType'); const fileExpire = document.getElementById('fileExpire'); const downloadFileBtn = document.getElementById('downloadFileBtn'); fileInfoDiv.style.display = 'block'; fileName.textContent = fileInfo.filename; fileSize.textContent = this.formatFileSize(fileInfo.size); fileType.textContent = fileInfo.file_type; if (fileInfo.is_expired) { fileExpire.textContent = '已过期'; fileExpire.style.color = 'var(--error-color)'; downloadFileBtn.disabled = true; downloadFileBtn.textContent = '文件已过期'; } else { const expireTime = new Date(fileInfo.expires_at); const now = new Date(); const diffMinutes = Math.ceil((expireTime - now) / (1000 * 60)); fileExpire.textContent = `剩余时间: ${diffMinutes}分钟`; fileExpire.style.color = 'var(--warning-color)'; downloadFileBtn.disabled = false; downloadFileBtn.textContent = '⬇️ 下载文件'; downloadFileBtn.onclick = () => this.handleFileDownload(fileInfo.code); } } // 隐藏文件信息 hideFileInfo() { const fileInfoDiv = document.getElementById('fileInfo'); fileInfoDiv.style.display = 'none'; } // 重置上传区域 resetUploadSection() { const dropZone = document.getElementById('dropZone'); const progressSection = document.getElementById('progressSection'); const uploadResult = document.getElementById('uploadResult'); const fileInput = document.getElementById('fileInput'); dropZone.style.display = 'block'; progressSection.style.display = 'none'; uploadResult.style.display = 'none'; if (fileInput) { fileInput.value = ''; } } // 重置文本区域 resetTextSection() { const textResult = document.getElementById('textResult'); const textInputArea = document.querySelector('.text-input-area'); const textContent = document.getElementById('textContent'); const textFilename = document.getElementById('textFilename'); textResult.style.display = 'none'; textInputArea.style.display = 'block'; textContent.value = ''; textFilename.value = 'shared_text.txt'; } // 复制到剪贴板 async copyToClipboard(elementId) { const element = document.getElementById(elementId); if (!element) return; const text = element.textContent; await this.copyTextToClipboard(text); } // 复制文本到剪贴板 async copyTextToClipboard(text) { try { await navigator.clipboard.writeText(text); this.showToast('已复制到剪贴板', 'success'); } catch (err) { // 降级处理 const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.opacity = '0'; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); this.showToast('已复制到剪贴板', 'success'); } catch (err) { this.showToast('复制失败', 'error'); } document.body.removeChild(textArea); } } // 格式化文件大小 formatFileSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } // 显示通知 showToast(message, type = 'success') { const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toastMessage'); if (!toast || !toastMessage) return; // 清除之前的类 toast.className = 'toast'; // 添加类型类 if (type !== 'success') { toast.classList.add(type); } toastMessage.textContent = message; toast.style.display = 'block'; // 3秒后自动隐藏 setTimeout(() => { toast.style.display = 'none'; }, 3000); } // 格式化时间 formatTime(dateString) { const date = new Date(dateString); return date.toLocaleString('zh-CN'); } // 计算剩余时间 calculateRemainingTime(expireTime) { const now = new Date(); const expire = new Date(expireTime); const diffMs = expire - now; if (diffMs <= 0) { return '已过期'; } const diffMinutes = Math.ceil(diffMs / (1000 * 60)); if (diffMinutes > 60) { const hours = Math.floor(diffMinutes / 60); const minutes = diffMinutes % 60; return `${hours}小时${minutes}分钟`; } return `${diffMinutes}分钟`; } } // 页面加载完成后初始化应用 document.addEventListener('DOMContentLoaded', () => { new FileShareApp(); }); // 工具函数:检测移动设备 function isMobile() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } // 工具函数:检测是否支持文件API function supportsFileAPI() { return window.File && window.FileReader && window.FileList && window.Blob; } // 工具函数:检测是否支持拖拽 function supportsDragAndDrop() { const div = document.createElement('div'); return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; } // 页面可见性变化时的处理 document.addEventListener('visibilitychange', () => { if (!document.hidden) { // 页面重新可见时,可以刷新一些数据 console.log('页面重新可见'); } }); // 全局错误处理 window.addEventListener('error', (e) => { console.error('全局错误:', e.error); }); // 未处理的Promise错误 window.addEventListener('unhandledrejection', (e) => { console.error('未处理的Promise错误:', e.reason); }); // 导出工具函数供其他脚本使用 window.FileShareUtils = { isMobile, supportsFileAPI, supportsDragAndDrop };