/** * 文件上传网站 - 前端交互逻辑 */ (function () { 'use strict'; // ── 常量 ──────────────────────────────────────────────── const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB const MAX_FILES = 9; const ALLOWED_EXTENSIONS = ['.txt', '.doc', '.docx', '.pdf', '.rtf', '.odt', '.html', '.csv', '.xls', '.xlsx']; // ── DOM 元素 ──────────────────────────────────────────── const $name = document.getElementById('name'); const $dropzone = document.getElementById('dropzone'); const $fileInput = document.getElementById('fileInput'); const $fileList = document.getElementById('fileList'); const $uploadBtn = document.getElementById('uploadBtn'); const $clearBtn = document.getElementById('clearBtn'); const $uploadForm = document.getElementById('uploadForm'); const $resultPanel = document.getElementById('resultPanel'); // 弹窗相关 const $modal = document.getElementById('captchaModal'); const $modalClose = document.getElementById('modalClose'); const $modalCancel = document.getElementById('modalCancel'); const $modalConfirm = document.getElementById('modalConfirm'); const $captchaImg = document.getElementById('captchaImg'); const $refreshCaptcha = document.getElementById('refreshCaptcha'); const $captchaInput = document.getElementById('captchaInput'); const $captchaError = document.getElementById('captchaError'); // ── 状态 ──────────────────────────────────────────────── let selectedFiles = []; // { file, id, status, error } let fileIdCounter = 0; let isUploading = false; // ── 工具函数 ──────────────────────────────────────────── function formatSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; } function getFileExt(filename) { const idx = filename.lastIndexOf('.'); return idx >= 0 ? filename.substring(idx).toLowerCase() : ''; } function validateFile(file) { const ext = getFileExt(file.name); if (!ALLOWED_EXTENSIONS.includes(ext)) { return '不支持的文件格式'; } if (file.size > MAX_FILE_SIZE) { return '文件超过 20MB 限制'; } if (file.size === 0) { return '文件为空'; } return null; } // ── 弹窗控制 ──────────────────────────────────────────── function openModal() { $captchaInput.value = ''; $captchaError.textContent = ''; refreshCaptcha(); $modal.classList.add('active'); setTimeout(() => $captchaInput.focus(), 200); } function closeModal() { $modal.classList.remove('active'); } function refreshCaptcha() { $captchaImg.src = '/api/captcha?' + Date.now(); $captchaInput.value = ''; $captchaError.textContent = ''; } // 弹窗事件绑定 $modalClose.addEventListener('click', closeModal); $modalCancel.addEventListener('click', closeModal); $captchaImg.addEventListener('click', refreshCaptcha); $refreshCaptcha.addEventListener('click', refreshCaptcha); // 点击遮罩层关闭 $modal.addEventListener('click', function (e) { if (e.target === $modal) closeModal(); }); // ESC 关闭弹窗 document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && $modal.classList.contains('active')) { closeModal(); } }); // 弹窗内回车 = 确认 $captchaInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); $modalConfirm.click(); } }); // 确认上传按钮 $modalConfirm.addEventListener('click', function () { const captchaVal = $captchaInput.value.trim(); if (!captchaVal) { $captchaError.textContent = '请输入验证码'; shakeElement($captchaInput); return; } const nameVal = $name.value.trim(); const validFiles = selectedFiles.filter(f => f.status !== 'error'); closeModal(); startUpload(nameVal, captchaVal, validFiles); }); // ── 文件选择(点击) ──────────────────────────────────── $dropzone.addEventListener('click', function () { if (!isUploading) { $fileInput.click(); } }); $fileInput.addEventListener('change', function () { addFiles(Array.from($fileInput.files)); $fileInput.value = ''; // 清空以便重复选择同名文件 }); // ── 文件拖拽 ──────────────────────────────────────────── $dropzone.addEventListener('dragover', function (e) { e.preventDefault(); if (!isUploading) { $dropzone.classList.add('dragover'); } }); $dropzone.addEventListener('dragleave', function (e) { e.preventDefault(); $dropzone.classList.remove('dragover'); }); $dropzone.addEventListener('drop', function (e) { e.preventDefault(); $dropzone.classList.remove('dragover'); if (!isUploading) { addFiles(Array.from(e.dataTransfer.files)); } }); // ── 添加文件到列表 ────────────────────────────────────── function addFiles(files) { if (isUploading) return; for (const file of files) { if (selectedFiles.length >= MAX_FILES) { showToast(`单次最多上传 ${MAX_FILES} 个文件`); break; } const error = validateFile(file); const item = { file: file, id: ++fileIdCounter, status: error ? 'error' : 'waiting', error: error, progress: 0 }; selectedFiles.push(item); } renderFileList(); updateButtonState(); } // ── 渲染文件列表 ──────────────────────────────────────── function renderFileList() { if (selectedFiles.length === 0) { $fileList.innerHTML = ''; return; } $fileList.innerHTML = selectedFiles.map(item => { const statusText = { waiting: '等待中', uploading: `${item.progress}%`, done: '已完成', error: item.error || '失败' }[item.status]; const statusClass = { waiting: 'status-waiting', uploading: 'status-uploading', done: 'status-done', error: 'status-error' }[item.status]; const iconSvg = item.status === 'done' ? '' : item.status === 'error' ? '' : ''; const progressBar = item.status === 'uploading' ? `
` : ''; const removeBtn = !isUploading ? `` : ''; return `已上传 ${data.success_count} 个文件至 ${data.storage_path}
` : ''} ${hasFail ? `${data.fail_count} 个文件上传失败
` : ''} ${hasSuccess ? `该目录累计文件数:${data.total_files}
` : ''}${message}