diff --git a/main.go b/main.go index 0324181..02ac652 100644 --- a/main.go +++ b/main.go @@ -391,6 +391,22 @@ func uploadFile(c *gin.Context) { path = "." } + dzuuid := c.PostForm("dzuuid") + chunkIndex := c.PostForm("dzchunkindex") + totalChunkCount := c.PostForm("dztotalchunkcount") + fileName := c.PostForm("dzanonymousfilename") + + isChunked := dzuuid != "" && chunkIndex != "" + + tmpDir := filepath.Join(rootDir, ".tmp", "chunks") + if err := os.MkdirAll(tmpDir, 0755); err != nil { + c.JSON(http.StatusBadRequest, UploadResponse{ + Success: false, + Message: err.Error(), + }) + return + } + file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, UploadResponse{ @@ -401,7 +417,12 @@ func uploadFile(c *gin.Context) { } defer file.Close() - fullPath := filepath.Join(rootDir, path, header.Filename) + targetFileName := header.Filename + if fileName != "" { + targetFileName = fileName + } + + fullPath := filepath.Join(rootDir, path, targetFileName) if !strings.HasPrefix(fullPath, rootDir) { c.JSON(http.StatusBadRequest, UploadResponse{ Success: false, @@ -410,24 +431,98 @@ func uploadFile(c *gin.Context) { return } - outFile, err := os.Create(fullPath) - if err != nil { - c.JSON(http.StatusBadRequest, UploadResponse{ - Success: false, - Message: err.Error(), + if isChunked { + idx := 0 + fmt.Sscanf(chunkIndex, "%d", &idx) + total := 0 + if totalChunkCount != "" { + fmt.Sscanf(totalChunkCount, "%d", &total) + } + + chunkDir := filepath.Join(tmpDir, dzuuid) + if err := os.MkdirAll(chunkDir, 0755); err != nil { + c.JSON(http.StatusBadRequest, UploadResponse{ + Success: false, + Message: err.Error(), + }) + return + } + + chunkPath := filepath.Join(chunkDir, fmt.Sprintf("chunk_%d", idx)) + outFile, err := os.Create(chunkPath) + if err != nil { + c.JSON(http.StatusBadRequest, UploadResponse{ + Success: false, + Message: err.Error(), + }) + return + } + defer outFile.Close() + + io.Copy(outFile, file) + + if idx == total-1 && total > 0 { + finalFile, err := os.Create(fullPath) + if err != nil { + os.RemoveAll(chunkDir) + c.JSON(http.StatusBadRequest, UploadResponse{ + Success: false, + Message: err.Error(), + }) + return + } + defer finalFile.Close() + + for i := 0; i < total; i++ { + chunkPath := filepath.Join(chunkDir, fmt.Sprintf("chunk_%d", i)) + chunkFile, err := os.Open(chunkPath) + if err != nil { + finalFile.Close() + os.RemoveAll(chunkDir) + c.JSON(http.StatusBadRequest, UploadResponse{ + Success: false, + Message: err.Error(), + }) + return + } + io.Copy(finalFile, chunkFile) + chunkFile.Close() + os.Remove(chunkPath) + } + os.RemoveAll(chunkDir) + + watchChan <- WatchEvent{Type: "create", Path: fullPath, Name: targetFileName} + + c.JSON(http.StatusOK, UploadResponse{ + Success: true, + Message: "上传成功", + }) + } else { + c.JSON(http.StatusOK, UploadResponse{ + Success: true, + Message: "分块上传成功", + }) + } + } else { + outFile, err := os.Create(fullPath) + if err != nil { + c.JSON(http.StatusBadRequest, UploadResponse{ + Success: false, + Message: err.Error(), + }) + return + } + defer outFile.Close() + + io.Copy(outFile, file) + + watchChan <- WatchEvent{Type: "create", Path: fullPath, Name: targetFileName} + + c.JSON(http.StatusOK, UploadResponse{ + Success: true, + Message: "上传成功", }) - return } - defer outFile.Close() - - io.Copy(outFile, file) - - watchChan <- WatchEvent{Type: "create", Path: fullPath, Name: header.Filename} - - c.JSON(http.StatusOK, UploadResponse{ - Success: true, - Message: "上传成功", - }) } func deleteFiles(c *gin.Context) { diff --git a/static/app.js b/static/app.js index 5127b67..6f0a338 100644 --- a/static/app.js +++ b/static/app.js @@ -1,11 +1,13 @@ (function() { 'use strict'; + Dropzone.autoDiscover = false; + const API_BASE = '/api'; let currentPath = '/'; let selectedFiles = new Set(); let eventSource = null; - let isUploading = false; + let uploadDropzone = null; const elements = { fileListBody: document.getElementById('file-list-body'), @@ -19,7 +21,6 @@ btnRefresh: document.getElementById('btn-refresh'), btnNewDir: document.getElementById('btn-new-dir'), selectAll: document.getElementById('select-all'), - fileInput: document.getElementById('file-input'), dropZone: document.getElementById('drop-zone'), previewModal: document.getElementById('preview-modal'), previewTitle: document.getElementById('preview-title'), @@ -47,23 +48,17 @@ renamePath: document.getElementById('rename-path'), uploadModal: document.getElementById('upload-modal'), uploadClose: document.getElementById('upload-close'), - uploadCancel: document.getElementById('upload-cancel'), - uploadConfirm: document.getElementById('upload-confirm'), - uploadList: document.getElementById('upload-list'), - uploadProgressContainer: document.getElementById('upload-progress-container'), - uploadProgressBar: document.getElementById('upload-progress-bar'), - uploadStats: document.getElementById('upload-stats'), - uploadSpeed: document.getElementById('upload-speed'), + uploadCloseBtn: document.getElementById('upload-close-btn'), + uploadPath: document.getElementById('upload-path'), searchInput: document.getElementById('search-input'), searchBtn: document.getElementById('search-btn'), notification: document.getElementById('notification') }; - let uploadXhrs = []; - function init() { loadFiles(currentPath); setupEventListeners(); + initDropzone(); startWatch(); } @@ -129,7 +124,6 @@ function getFileIcon(name, isDir) { if (isDir) return '📁'; - var ext = name.split('.').pop().toLowerCase(); var icons = { pdf: '📕', doc: '📘', docx: '📘', @@ -160,7 +154,6 @@ var date = new Date(timeStr); var now = new Date(); var isToday = date.toDateString() === now.toDateString(); - if (isToday) { return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } @@ -178,15 +171,12 @@ elements.breadcrumb.innerHTML = '根目录'; return; } - var parts = path.split('/').filter(function(p) { return p; }); var html = '根目录'; - parts.forEach(function(part, index) { - var currentPath = '/' + parts.slice(0, index + 1).join('/'); - html += '' + escapeHtml(part) + ''; + var currentPathStr = '/' + parts.slice(0, index + 1).join('/'); + html += '' + escapeHtml(part) + ''; }); - elements.breadcrumb.innerHTML = html; } @@ -197,6 +187,222 @@ elements.btnMove.disabled = count !== 1; } + function initDropzone() { + var dropzoneElement = document.querySelector('#upload-dropzone'); + if (dropzoneElement) { + var existingDropzone = dropzoneElement.dropzone; + if (existingDropzone) { + existingDropzone.destroy(); + } + } + + if (uploadDropzone) { + uploadDropzone.destroy(); + uploadDropzone = null; + } + + uploadDropzone = new Dropzone('#upload-dropzone', { + url: API_BASE + '/upload', + paramName: 'file', + maxFilesize: 0, + timeout: 0, + chunking: true, + parallelUploads: 2, + parallelUploadsPerFile: 1, + addRemoveLinks: false, + maxChunkSize: 1024 * 1024, + retryChunks: true, + retryChunksLimit: 3, + previewTemplate: '
拖拽文件到此处或点击上传
', + dictDefaultMessage: '拖拽文件到此处或点击上传', + dictRemoveFile: '移除', + dictCancelUpload: '取消', + dictCancelUploadConfirmation: '确定取消上传?', + dictFallbackMessage: '您的浏览器不支持拖拽上传', + dictFileTooBig: '文件太大 ({{filesize}}MB),最大限制: {{maxFilesize}}MB', + dictInvalidFileType: '不支持的文件类型', + dictResponseError: '服务器错误', + dictUploadCanceled: '上传已取消', + acceptedFiles: null, + + init: function() { + var dz = this; + var uploadedChunks = {}; + + function getFileProgress(file) { + return { + uploaded: file.uploadedChunks || 0, + total: file.totalChunks || 1 + }; + } + + this.on('addedfile', function(file) { + var list = document.getElementById('upload-file-list'); + var icon = getFileIcon(file.name); + var size = formatFileSize(file.size); + var fileId = 'f' + Date.now() + Math.random().toString(36).substr(2, 5); + + file._dzFileId = fileId; + file._uploadProgress = 0; + file._resumeSupported = true; + + var item = document.createElement('div'); + item.className = 'upload-file-item'; + item.id = 'upload-item-' + fileId; + item.innerHTML = '' + icon + '' + + '
' + + '
' + escapeHtml(file.name) + '
' + + '
' + size + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '0%' + + '
' + + '
' + + '' + + '' + + '' + + '
' + + '' + + '
'; + list.appendChild(item); + + var progressEl = document.getElementById('progress-' + fileId); + var percentEl = document.getElementById('percent-' + fileId); + var chunksEl = document.getElementById('chunks-' + fileId); + + if (file.totalChunks > 1) { + chunksEl.textContent = '0/' + file.totalChunks; + } + + item.querySelector('.dz-remove').addEventListener('click', function() { + dz.removeFile(file); + var el = document.getElementById('upload-item-' + fileId); + if (el) el.remove(); + }); + }); + + this.on('uploadprogress', function(file, progress, bytesSent, totalBytesSent, totalBytes) { + var fileId = file._dzFileId; + var progressEl = document.getElementById('progress-' + fileId); + var percentEl = document.getElementById('percent-' + fileId); + var chunksEl = document.getElementById('chunks-' + fileId); + + if (file.totalChunks > 1) { + var currentChunk = file.upload.chunk || 0; + var chunkWeight = 100 / file.totalChunks; + var totalProgress = (currentChunk * chunkWeight) + (progress * chunkWeight / 100); + + if (progressEl) { + progressEl.style.width = Math.min(totalProgress, 100) + '%'; + } + if (percentEl) { + percentEl.textContent = Math.round(Math.min(totalProgress, 100)) + '%'; + } + if (chunksEl) { + chunksEl.textContent = (currentChunk + 1) + '/' + file.totalChunks; + } + } else { + if (progressEl) { + progressEl.style.width = progress + '%'; + } + if (percentEl) { + percentEl.textContent = Math.round(progress) + '%'; + } + } + }); + + this.on('uploadprogress', function(file, progress, bytesSent, totalBytesSent, totalBytes) { + var fileId = file._dzFileId; + var progressEl = document.getElementById('progress-' + fileId); + var percentEl = document.getElementById('percent-' + fileId); + var chunksEl = document.getElementById('chunks-' + fileId); + + file._uploadProgress = progress; + + if (file.totalChunks > 1) { + var currentChunk = file.upload.chunk || 0; + var chunkProgress = (currentChunk / file.totalChunks) * 100; + var currentChunkProgress = (progress / 100) * (100 / file.totalChunks); + var totalProgress = chunkProgress + currentChunkProgress; + + if (progressEl) { + progressEl.style.width = Math.min(totalProgress, 100) + '%'; + } + if (percentEl) { + percentEl.textContent = Math.round(Math.min(totalProgress, 100)) + '%'; + } + if (chunksEl) { + chunksEl.textContent = (currentChunk + 1) + '/' + file.totalChunks; + } + } else { + if (progressEl) { + progressEl.style.width = progress + '%'; + } + if (percentEl) { + percentEl.textContent = Math.round(progress) + '%'; + } + } + }); + + this.on('success', function(file, response) { + var fileId = file._dzFileId; + var itemEl = document.getElementById('upload-item-' + fileId); + if (itemEl) { + var progressEl = document.getElementById('progress-' + fileId); + var percentEl = document.getElementById('percent-' + fileId); + var chunksEl = document.getElementById('chunks-' + fileId); + + progressEl.style.width = '100%'; + percentEl.textContent = '100%'; + if (chunksEl && file.totalChunks > 1) { + chunksEl.textContent = file.totalChunks + '/' + file.totalChunks; + } + + try { + var data = JSON.parse(response); + if (data.success) { + itemEl.classList.add('dz-success'); + } else if (data.merged) { + itemEl.classList.add('dz-success'); + } else { + itemEl.classList.add('dz-error'); + itemEl.querySelector('.dz-error-message').textContent = data.message || '上传失败'; + } + } catch (e) { + itemEl.classList.add('dz-success'); + } + } + }); + + this.on('error', function(file, message) { + var fileId = file._dzFileId; + var itemEl = document.getElementById('upload-item-' + fileId); + if (itemEl) { + itemEl.classList.add('dz-error'); + itemEl.querySelector('.dz-error-message').textContent = message || '上传失败'; + } + }); + + this.on('complete', function(file) { + if (dz.getUploadingFiles().length === 0 && dz.getQueuedFiles().length === 0) { + setTimeout(function() { + loadFiles(currentPath); + }, 500); + } + }); + + this.on('queuecomplete', function() { + setTimeout(function() { + loadFiles(currentPath); + }, 500); + }); + } + }); + } + function setupEventListeners() { elements.breadcrumb.addEventListener('click', function(e) { if (e.target.classList.contains('crumb')) { @@ -230,7 +436,6 @@ } else if (actionBtn) { var action = actionBtn.dataset.action; var path = actionBtn.dataset.path; - if (action === 'download') downloadFile(path); if (action === 'preview') previewFile(path); if (action === 'rename') showRenameModal(path); @@ -246,10 +451,22 @@ }); elements.btnUpload.addEventListener('click', function() { - elements.fileInput.click(); + var list = document.getElementById('upload-file-list'); + if (list) { + list.innerHTML = ''; + } + if (uploadDropzone) { + uploadDropzone.removeAllFiles(true); + } + var pathInput = document.getElementById('upload-path'); + if (pathInput) { + pathInput.value = currentPath; + } + elements.uploadModal.classList.add('active'); }); elements.btnDownload.addEventListener('click', downloadSelected); + elements.btnDelete.addEventListener('click', function() { if (selectedFiles.size > 0 && confirm('确定要删除选中的 ' + selectedFiles.size + ' 个项目吗?')) { deleteFiles(Array.from(selectedFiles)); @@ -272,11 +489,6 @@ elements.newDirName.focus(); }); - elements.fileInput.addEventListener('change', function(e) { - uploadFiles(e.target.files); - e.target.value = ''; - }); - elements.searchBtn.addEventListener('click', searchFiles); elements.searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') searchFiles(); @@ -342,7 +554,14 @@ elements.dropZone.addEventListener('drop', function(e) { e.preventDefault(); elements.dropZone.classList.remove('active'); - uploadFiles(e.dataTransfer.files); + if (uploadDropzone) { + uploadDropzone.removeAllFiles(true); + e.dataTransfer.files.forEach(function(file) { + uploadDropzone.addFile(file); + }); + elements.uploadPath.value = currentPath; + elements.uploadModal.classList.add('active'); + } }); } @@ -390,21 +609,10 @@ }); elements.uploadClose.addEventListener('click', function() { - if (!isUploading) { - elements.uploadModal.classList.remove('active'); - } + elements.uploadModal.classList.remove('active'); }); - elements.uploadCancel.addEventListener('click', function() { - if (isUploading) { - uploadXhrs.forEach(function(xhr) { xhr.abort(); }); - uploadXhrs = []; - isUploading = false; - elements.uploadModal.classList.remove('active'); - } - }); - - elements.uploadConfirm.addEventListener('click', function() { + elements.uploadCloseBtn.addEventListener('click', function() { elements.uploadModal.classList.remove('active'); }); @@ -413,151 +621,7 @@ if (e.target === elements.moveModal) elements.moveModal.classList.remove('active'); if (e.target === elements.newDirModal) elements.newDirModal.classList.remove('active'); if (e.target === elements.renameModal) elements.renameModal.classList.remove('active'); - if (e.target === elements.uploadModal && !isUploading) elements.uploadModal.classList.remove('active'); - }); - } - - function uploadFiles(files) { - if (!files || files.length === 0) return; - - var fileArray = Array.from(files); - - elements.uploadList.innerHTML = fileArray.map(function(file, index) { - return '
' + - '' + getFileIcon(file.name, false) + '' + - '
' + - '
' + escapeHtml(file.name) + '
' + - '
' + - '' + formatFileSize(file.size) + '' + - '等待中...' + - '
' + - '
' + - '
' + - '
'; - }).join(''); - - elements.uploadProgressContainer.classList.add('active'); - elements.uploadProgressBar.style.width = '0%'; - elements.uploadStats.classList.add('active'); - elements.uploadSpeed.classList.add('active'); - elements.uploadSpeed.textContent = '准备上传...'; - elements.uploadCancel.style.display = 'block'; - elements.uploadConfirm.style.display = 'none'; - elements.uploadModal.classList.add('active'); - - var total = fileArray.length; - var totalBytes = fileArray.reduce(function(sum, f) { return sum + f.size; }, 0); - var uploadedBytes = 0; - var completedCount = 0; - uploadXhrs = []; - - var loadedBytesMap = fileArray.map(function() { return 0; }); - var finishedMap = fileArray.map(function() { return false; }); - var lastTime = Date.now(); - var lastLoaded = 0; - - function updateSpeed() { - var now = Date.now(); - var timeDiff = (now - lastTime) / 1000; - if (timeDiff < 0.5) return; - - var currentLoaded = loadedBytesMap.reduce(function(sum, bytes, i) { - return sum + (finishedMap[i] ? fileArray[i].size : bytes); - }, 0); - var bytesDiff = currentLoaded - lastLoaded; - var speed = bytesDiff / timeDiff; - - if (speed > 0 && completedCount < total) { - var remaining = totalBytes - currentLoaded; - var eta = remaining / speed; - var etaStr; - if (eta < 60) etaStr = Math.ceil(eta) + '秒'; - else if (eta < 3600) etaStr = Math.ceil(eta / 60) + '分钟'; - else etaStr = Math.ceil(eta / 3600) + '小时'; - - elements.uploadSpeed.textContent = formatFileSize(speed) + '/s · 剩余' + etaStr; - } else if (completedCount < total) { - elements.uploadSpeed.textContent = '处理中...'; - } - - lastTime = now; - lastLoaded = currentLoaded; - } - - fileArray.forEach(function(file, index) { - var formData = new FormData(); - formData.append('file', file); - - var xhr = new XMLHttpRequest(); - uploadXhrs.push(xhr); - - xhr.upload.addEventListener('progress', function(e) { - if (e.lengthComputable) { - loadedBytesMap[index] = e.loaded; - var percent = Math.min(99, Math.round((e.loaded / e.total) * 100)); - - document.getElementById('upload-progress-text-' + index).textContent = percent + '%'; - document.getElementById('upload-item-progress-' + index).style.width = percent + '%'; - - var currentLoaded = loadedBytesMap.reduce(function(sum, bytes, i) { - return sum + (finishedMap[i] ? fileArray[i].size : bytes); - }, 0); - var allPercent = Math.min(99, Math.round((currentLoaded / totalBytes) * 100)); - elements.uploadProgressBar.style.width = allPercent + '%'; - - updateSpeed(); - } - }); - - xhr.addEventListener('load', function() { - finishedMap[index] = true; - loadedBytesMap[index] = file.size; - uploadedBytes += file.size; - completedCount++; - - document.getElementById('upload-progress-text-' + index).textContent = '完成'; - document.getElementById('upload-item-progress-' + index).style.background = '#4caf50'; - document.getElementById('upload-item-progress-' + index).style.width = '100%'; - - var allPercent = Math.round((uploadedBytes / totalBytes) * 100); - elements.uploadProgressBar.style.width = allPercent + '%'; - - if (completedCount === total) { - isUploading = false; - elements.uploadCancel.style.display = 'none'; - elements.uploadConfirm.style.display = 'block'; - elements.uploadStats.textContent = '上传完成: 成功 ' + completedCount + ' 个'; - elements.uploadSpeed.textContent = ''; - loadFiles(currentPath); - } else { - elements.uploadSpeed.textContent = '处理中... (' + (total - completedCount) + '个文件)'; - } - }); - - xhr.addEventListener('error', function() { - finishedMap[index] = true; - loadedBytesMap[index] = file.size; - uploadedBytes += file.size; - completedCount++; - - document.getElementById('upload-progress-text-' + index).textContent = '失败'; - document.getElementById('upload-item-progress-' + index).style.background = '#f44336'; - - var allPercent = Math.round((uploadedBytes / totalBytes) * 100); - elements.uploadProgressBar.style.width = allPercent + '%'; - - if (completedCount === total) { - isUploading = false; - elements.uploadCancel.style.display = 'none'; - elements.uploadConfirm.style.display = 'block'; - elements.uploadStats.textContent = '上传完成: 成功 ' + completedCount + ' 个'; - elements.uploadSpeed.textContent = ''; - loadFiles(currentPath); - } - }); - - xhr.open('POST', API_BASE + '/upload?path=' + encodeURIComponent(currentPath)); - xhr.send(formData); + if (e.target === elements.uploadModal) elements.uploadModal.classList.remove('active'); }); } @@ -625,16 +689,13 @@ function renderDirectoryTree(files, currentPath) { var dirs = (files || []).filter(function(f) { return f.isDir; }); - if (dirs.length === 0) { elements.dirTree.innerHTML = '
📂

此目录为空

'; return; } - elements.dirTree.innerHTML = dirs.map(function(dir) { return '
📁' + escapeHtml(dir.name) + '
'; }).join(''); - elements.dirTree.querySelectorAll('.dir-item').forEach(function(item) { item.addEventListener('click', function() { loadDirectoryTree(item.dataset.path); @@ -647,17 +708,13 @@ elements.moveBreadcrumb.innerHTML = '根目录'; return; } - var parts = path.split('/').filter(function(p) { return p; }); var html = '根目录'; - parts.forEach(function(part, index) { - var currentPath = '/' + parts.slice(0, index + 1).join('/'); - html += '' + escapeHtml(part) + ''; + var currentPathStr = '/' + parts.slice(0, index + 1).join('/'); + html += '' + escapeHtml(part) + ''; }); - elements.moveBreadcrumb.innerHTML = html; - elements.moveBreadcrumb.querySelectorAll('.crumb').forEach(function(crumb) { crumb.addEventListener('click', function() { loadDirectoryTree(crumb.dataset.path); @@ -668,17 +725,14 @@ function moveFile() { var sourcePath = elements.moveSource.value; var destPath = elements.moveDest.value; - if (!sourcePath || !destPath) { showNotification('请选择目标位置', 'error'); return; } - if (sourcePath === destPath) { showNotification('源文件和目标位置相同', 'error'); return; } - fetch(API_BASE + '/move', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -713,14 +767,11 @@ function renameFile() { var path = elements.renamePath.value; var newName = elements.renameName.value.trim(); - if (!path || !newName) { showNotification('请输入新名称', 'error'); return; } - var newPath = path.substring(0, path.lastIndexOf('/') + 1) + newName; - fetch(API_BASE + '/rename', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -751,9 +802,7 @@ showNotification('请输入文件夹名称', 'error'); return; } - var path = currentPath === '/' ? '/' + name : currentPath + '/' + name; - fetch(API_BASE + '/dir', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -779,7 +828,6 @@ elements.previewTitle.textContent = name; elements.previewBody.innerHTML = '
📄

加载中...

'; elements.previewModal.classList.add('active'); - fetch(API_BASE + '/preview?path=' + encodeURIComponent(path)) .then(function(response) { var contentType = response.headers.get('Content-Type'); @@ -822,21 +870,17 @@ eventSource.close(); eventSource = null; } - try { eventSource = new EventSource(API_BASE + '/watch'); - eventSource.onopen = function() { elements.connectionStatus.textContent = '● 已连接'; elements.connectionStatus.className = 'connected'; }; - eventSource.onerror = function() { if (eventSource.readyState === EventSource.CLOSED) return; elements.connectionStatus.textContent = '● 已连接'; elements.connectionStatus.className = 'connected'; }; - eventSource.addEventListener('message', function(e) { try { var event = JSON.parse(e.data); @@ -852,12 +896,9 @@ function handleWatchEvent(event) { var inCurrentDir = currentPath === '/' || event.path.startsWith(currentPath + '/') || event.path === currentPath; if (!inCurrentDir) return; - var pathParts = event.path.split('/').filter(function(p) { return p; }); var eventDir = '/' + pathParts.slice(0, -1).join('/') || '/'; - if (eventDir !== currentPath) return; - switch (event.type) { case 'create': showNotification('新建文件: ' + event.name, 'info'); @@ -895,9 +936,7 @@ loadFiles(currentPath); return; } - elements.fileListBody.innerHTML = '搜索中...'; - searchAllDirectories('/', keyword.toLowerCase(), []); } @@ -911,7 +950,6 @@ results.push(file); } }); - var dirs = files.filter(function(f) { return f.isDir; }); if (dirs.length > 0) { var promises = dirs.map(function(dir) { @@ -939,7 +977,6 @@ function renderSearchResults(files, keyword) { var sortedFiles = files.sort(function(a, b) { return a.name.localeCompare(b.name); }); - var html = sortedFiles.map(function(file) { return '' + '' + @@ -953,7 +990,6 @@ '' + ''; }).join(''); - elements.fileListBody.innerHTML = html; updateButtonStates(); } diff --git a/static/dropzone.min.css b/static/dropzone.min.css new file mode 100644 index 0000000..e8f4be4 --- /dev/null +++ b/static/dropzone.min.css @@ -0,0 +1 @@ +@-webkit-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-moz-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-moz-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-moz-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:2px solid rgba(0,0,0,.3);background:#fff;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:.5}.dropzone .dz-message{text-align:center;margin:2em 0}.dropzone .dz-message .dz-button{background:none;color:inherit;border:none;padding:0;font:inherit;cursor:pointer;outline:inherit}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:#fff}.dropzone .dz-preview.dz-image-preview .dz-details{-webkit-transition:opacity 0.2s linear;-moz-transition:opacity 0.2s linear;-ms-transition:opacity 0.2s linear;-o-transition:opacity 0.2s linear;transition:opacity 0.2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,.8);background-color:rgba(255,255,255,.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid transparent}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:rgba(255,255,255,.4);padding:0 .4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{-webkit-transform:scale(1.05, 1.05);-moz-transform:scale(1.05, 1.05);-ms-transform:scale(1.05, 1.05);-o-transform:scale(1.05, 1.05);transform:scale(1.05, 1.05);-webkit-filter:blur(8px);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{-webkit-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;-webkit-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;-webkit-transition:all 0.2s linear;-moz-transition:all 0.2s linear;-ms-transition:all 0.2s linear;-o-transition:all 0.2s linear;transition:all 0.2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;-webkit-transition:opacity 0.4s ease-in;-moz-transition:opacity 0.4s ease-in;-ms-transition:opacity 0.4s ease-in;-o-transition:opacity 0.4s ease-in;transition:opacity 0.4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{-webkit-animation:pulse 6s ease infinite;-moz-animation:pulse 6s ease infinite;-ms-animation:pulse 6s ease infinite;-o-animation:pulse 6s ease infinite;animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:16px;left:50%;top:50%;margin-top:-8px;width:80px;margin-left:-40px;background:rgba(255,255,255,.9);-webkit-transform:scale(1);border-radius:8px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#333;background:linear-gradient(to bottom, #666, #444);position:absolute;top:0;left:0;bottom:0;width:0;-webkit-transition:width 300ms ease-in-out;-moz-transition:width 300ms ease-in-out;-ms-transition:width 300ms ease-in-out;-o-transition:width 300ms ease-in-out;transition:width 300ms ease-in-out}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;-webkit-transition:opacity 0.3s ease;-moz-transition:opacity 0.3s ease;-ms-transition:opacity 0.3s ease;-o-transition:opacity 0.3s ease;transition:opacity 0.3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#be2626;background:linear-gradient(to bottom, #be2626, #a92222);padding:.5em 1.2em;color:#fff}.dropzone .dz-preview .dz-error-message:after{content:"";position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #be2626} \ No newline at end of file diff --git a/static/dropzone.min.js b/static/dropzone.min.js new file mode 100644 index 0000000..94e32e3 --- /dev/null +++ b/static/dropzone.min.js @@ -0,0 +1 @@ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}(self,(function(){return function(){var e={3099:function(e){e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},6077:function(e,t,n){var r=n(111);e.exports=function(e){if(!r(e)&&null!==e)throw TypeError("Can't set "+String(e)+" as a prototype");return e}},1223:function(e,t,n){var r=n(5112),i=n(30),o=n(3070),a=r("unscopables"),u=Array.prototype;null==u[a]&&o.f(u,a,{configurable:!0,value:i(null)}),e.exports=function(e){u[a][e]=!0}},1530:function(e,t,n){"use strict";var r=n(8710).charAt;e.exports=function(e,t,n){return t+(n?r(e,t).length:1)}},5787:function(e){e.exports=function(e,t,n){if(!(e instanceof t))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation");return e}},9670:function(e,t,n){var r=n(111);e.exports=function(e){if(!r(e))throw TypeError(String(e)+" is not an object");return e}},4019:function(e){e.exports="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof DataView},260:function(e,t,n){"use strict";var r,i=n(4019),o=n(9781),a=n(7854),u=n(111),s=n(6656),l=n(648),c=n(8880),f=n(1320),p=n(3070).f,h=n(9518),d=n(7674),v=n(5112),y=n(9711),g=a.Int8Array,m=g&&g.prototype,b=a.Uint8ClampedArray,x=b&&b.prototype,w=g&&h(g),E=m&&h(m),k=Object.prototype,A=k.isPrototypeOf,S=v("toStringTag"),F=y("TYPED_ARRAY_TAG"),T=i&&!!d&&"Opera"!==l(a.opera),C=!1,L={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},R={BigInt64Array:8,BigUint64Array:8},I=function(e){if(!u(e))return!1;var t=l(e);return s(L,t)||s(R,t)};for(r in L)a[r]||(T=!1);if((!T||"function"!=typeof w||w===Function.prototype)&&(w=function(){throw TypeError("Incorrect invocation")},T))for(r in L)a[r]&&d(a[r],w);if((!T||!E||E===k)&&(E=w.prototype,T))for(r in L)a[r]&&d(a[r].prototype,E);if(T&&h(x)!==E&&d(x,E),o&&!s(E,S))for(r in C=!0,p(E,S,{get:function(){return u(this)?this[F]:void 0}}),L)a[r]&&c(a[r],F,r);e.exports={NATIVE_ARRAY_BUFFER_VIEWS:T,TYPED_ARRAY_TAG:C&&F,aTypedArray:function(e){if(I(e))return e;throw TypeError("Target is not a typed array")},aTypedArrayConstructor:function(e){if(d){if(A.call(w,e))return e}else for(var t in L)if(s(L,r)){var n=a[t];if(n&&(e===n||A.call(n,e)))return e}throw TypeError("Target is not a typed array constructor")},exportTypedArrayMethod:function(e,t,n){if(o){if(n)for(var r in L){var i=a[r];i&&s(i.prototype,e)&&delete i.prototype[e]}E[e]&&!n||f(E,e,n?t:T&&m[e]||t)}},exportTypedArrayStaticMethod:function(e,t,n){var r,i;if(o){if(d){if(n)for(r in L)(i=a[r])&&s(i,e)&&delete i[e];if(w[e]&&!n)return;try{return f(w,e,n?t:T&&g[e]||t)}catch(e){}}for(r in L)!(i=a[r])||i[e]&&!n||f(i,e,t)}},isView:function(e){if(!u(e))return!1;var t=l(e);return"DataView"===t||s(L,t)||s(R,t)},isTypedArray:I,TypedArray:w,TypedArrayPrototype:E}},3331:function(e,t,n){"use strict";var r=n(7854),i=n(9781),o=n(4019),a=n(8880),u=n(2248),s=n(7293),l=n(5787),c=n(9958),f=n(7466),p=n(7067),h=n(1179),d=n(9518),v=n(7674),y=n(8006).f,g=n(3070).f,m=n(1285),b=n(8003),x=n(9909),w=x.get,E=x.set,k="ArrayBuffer",A="DataView",S="Wrong index",F=r.ArrayBuffer,T=F,C=r.DataView,L=C&&C.prototype,R=Object.prototype,I=r.RangeError,U=h.pack,O=h.unpack,_=function(e){return[255&e]},M=function(e){return[255&e,e>>8&255]},z=function(e){return[255&e,e>>8&255,e>>16&255,e>>24&255]},P=function(e){return e[3]<<24|e[2]<<16|e[1]<<8|e[0]},j=function(e){return U(e,23,4)},D=function(e){return U(e,52,8)},N=function(e,t){g(e.prototype,t,{get:function(){return w(this)[t]}})},B=function(e,t,n,r){var i=p(n),o=w(e);if(i+t>o.byteLength)throw I(S);var a=w(o.buffer).bytes,u=i+o.byteOffset,s=a.slice(u,u+t);return r?s:s.reverse()},q=function(e,t,n,r,i,o){var a=p(n),u=w(e);if(a+t>u.byteLength)throw I(S);for(var s=w(u.buffer).bytes,l=a+u.byteOffset,c=r(+i),f=0;fG;)(W=Y[G++])in T||a(T,W,F[W]);H.constructor=T}v&&d(L)!==R&&v(L,R);var Q=new C(new T(2)),$=L.setInt8;Q.setInt8(0,2147483648),Q.setInt8(1,2147483649),!Q.getInt8(0)&&Q.getInt8(1)||u(L,{setInt8:function(e,t){$.call(this,e,t<<24>>24)},setUint8:function(e,t){$.call(this,e,t<<24>>24)}},{unsafe:!0})}else T=function(e){l(this,T,k);var t=p(e);E(this,{bytes:m.call(new Array(t),0),byteLength:t}),i||(this.byteLength=t)},C=function(e,t,n){l(this,C,A),l(e,T,A);var r=w(e).byteLength,o=c(t);if(o<0||o>r)throw I("Wrong offset");if(o+(n=void 0===n?r-o:f(n))>r)throw I("Wrong length");E(this,{buffer:e,byteLength:n,byteOffset:o}),i||(this.buffer=e,this.byteLength=n,this.byteOffset=o)},i&&(N(T,"byteLength"),N(C,"buffer"),N(C,"byteLength"),N(C,"byteOffset")),u(C.prototype,{getInt8:function(e){return B(this,1,e)[0]<<24>>24},getUint8:function(e){return B(this,1,e)[0]},getInt16:function(e){var t=B(this,2,e,arguments.length>1?arguments[1]:void 0);return(t[1]<<8|t[0])<<16>>16},getUint16:function(e){var t=B(this,2,e,arguments.length>1?arguments[1]:void 0);return t[1]<<8|t[0]},getInt32:function(e){return P(B(this,4,e,arguments.length>1?arguments[1]:void 0))},getUint32:function(e){return P(B(this,4,e,arguments.length>1?arguments[1]:void 0))>>>0},getFloat32:function(e){return O(B(this,4,e,arguments.length>1?arguments[1]:void 0),23)},getFloat64:function(e){return O(B(this,8,e,arguments.length>1?arguments[1]:void 0),52)},setInt8:function(e,t){q(this,1,e,_,t)},setUint8:function(e,t){q(this,1,e,_,t)},setInt16:function(e,t){q(this,2,e,M,t,arguments.length>2?arguments[2]:void 0)},setUint16:function(e,t){q(this,2,e,M,t,arguments.length>2?arguments[2]:void 0)},setInt32:function(e,t){q(this,4,e,z,t,arguments.length>2?arguments[2]:void 0)},setUint32:function(e,t){q(this,4,e,z,t,arguments.length>2?arguments[2]:void 0)},setFloat32:function(e,t){q(this,4,e,j,t,arguments.length>2?arguments[2]:void 0)},setFloat64:function(e,t){q(this,8,e,D,t,arguments.length>2?arguments[2]:void 0)}});b(T,k),b(C,A),e.exports={ArrayBuffer:T,DataView:C}},1048:function(e,t,n){"use strict";var r=n(7908),i=n(1400),o=n(7466),a=Math.min;e.exports=[].copyWithin||function(e,t){var n=r(this),u=o(n.length),s=i(e,u),l=i(t,u),c=arguments.length>2?arguments[2]:void 0,f=a((void 0===c?u:i(c,u))-l,u-s),p=1;for(l0;)l in n?n[s]=n[l]:delete n[s],s+=p,l+=p;return n}},1285:function(e,t,n){"use strict";var r=n(7908),i=n(1400),o=n(7466);e.exports=function(e){for(var t=r(this),n=o(t.length),a=arguments.length,u=i(a>1?arguments[1]:void 0,n),s=a>2?arguments[2]:void 0,l=void 0===s?n:i(s,n);l>u;)t[u++]=e;return t}},8533:function(e,t,n){"use strict";var r=n(2092).forEach,i=n(9341)("forEach");e.exports=i?[].forEach:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}},8457:function(e,t,n){"use strict";var r=n(9974),i=n(7908),o=n(3411),a=n(7659),u=n(7466),s=n(6135),l=n(1246);e.exports=function(e){var t,n,c,f,p,h,d=i(e),v="function"==typeof this?this:Array,y=arguments.length,g=y>1?arguments[1]:void 0,m=void 0!==g,b=l(d),x=0;if(m&&(g=r(g,y>2?arguments[2]:void 0,2)),null==b||v==Array&&a(b))for(n=new v(t=u(d.length));t>x;x++)h=m?g(d[x],x):d[x],s(n,x,h);else for(p=(f=b.call(d)).next,n=new v;!(c=p.call(f)).done;x++)h=m?o(f,g,[c.value,x],!0):c.value,s(n,x,h);return n.length=x,n}},1318:function(e,t,n){var r=n(5656),i=n(7466),o=n(1400),a=function(e){return function(t,n,a){var u,s=r(t),l=i(s.length),c=o(a,l);if(e&&n!=n){for(;l>c;)if((u=s[c++])!=u)return!0}else for(;l>c;c++)if((e||c in s)&&s[c]===n)return e||c||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},2092:function(e,t,n){var r=n(9974),i=n(8361),o=n(7908),a=n(7466),u=n(5417),s=[].push,l=function(e){var t=1==e,n=2==e,l=3==e,c=4==e,f=6==e,p=7==e,h=5==e||f;return function(d,v,y,g){for(var m,b,x=o(d),w=i(x),E=r(v,y,3),k=a(w.length),A=0,S=g||u,F=t?S(d,k):n||p?S(d,0):void 0;k>A;A++)if((h||A in w)&&(b=E(m=w[A],A,x),e))if(t)F[A]=b;else if(b)switch(e){case 3:return!0;case 5:return m;case 6:return A;case 2:s.call(F,m)}else switch(e){case 4:return!1;case 7:s.call(F,m)}return f?-1:l||c?c:F}};e.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterOut:l(7)}},6583:function(e,t,n){"use strict";var r=n(5656),i=n(9958),o=n(7466),a=n(9341),u=Math.min,s=[].lastIndexOf,l=!!s&&1/[1].lastIndexOf(1,-0)<0,c=a("lastIndexOf"),f=l||!c;e.exports=f?function(e){if(l)return s.apply(this,arguments)||0;var t=r(this),n=o(t.length),a=n-1;for(arguments.length>1&&(a=u(a,i(arguments[1]))),a<0&&(a=n+a);a>=0;a--)if(a in t&&t[a]===e)return a||0;return-1}:s},1194:function(e,t,n){var r=n(7293),i=n(5112),o=n(7392),a=i("species");e.exports=function(e){return o>=51||!r((function(){var t=[];return(t.constructor={})[a]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},9341:function(e,t,n){"use strict";var r=n(7293);e.exports=function(e,t){var n=[][e];return!!n&&r((function(){n.call(null,t||function(){throw 1},1)}))}},3671:function(e,t,n){var r=n(3099),i=n(7908),o=n(8361),a=n(7466),u=function(e){return function(t,n,u,s){r(n);var l=i(t),c=o(l),f=a(l.length),p=e?f-1:0,h=e?-1:1;if(u<2)for(;;){if(p in c){s=c[p],p+=h;break}if(p+=h,e?p<0:f<=p)throw TypeError("Reduce of empty array with no initial value")}for(;e?p>=0:f>p;p+=h)p in c&&(s=n(s,c[p],p,l));return s}};e.exports={left:u(!1),right:u(!0)}},5417:function(e,t,n){var r=n(111),i=n(3157),o=n(5112)("species");e.exports=function(e,t){var n;return i(e)&&("function"!=typeof(n=e.constructor)||n!==Array&&!i(n.prototype)?r(n)&&null===(n=n[o])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===t?0:t)}},3411:function(e,t,n){var r=n(9670),i=n(9212);e.exports=function(e,t,n,o){try{return o?t(r(n)[0],n[1]):t(n)}catch(t){throw i(e),t}}},7072:function(e,t,n){var r=n(5112)("iterator"),i=!1;try{var o=0,a={next:function(){return{done:!!o++}},return:function(){i=!0}};a[r]=function(){return this},Array.from(a,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!i)return!1;var n=!1;try{var o={};o[r]=function(){return{next:function(){return{done:n=!0}}}},e(o)}catch(e){}return n}},4326:function(e){var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},648:function(e,t,n){var r=n(1694),i=n(4326),o=n(5112)("toStringTag"),a="Arguments"==i(function(){return arguments}());e.exports=r?i:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?n:a?i(t):"Object"==(r=i(t))&&"function"==typeof t.callee?"Arguments":r}},9920:function(e,t,n){var r=n(6656),i=n(3887),o=n(1236),a=n(3070);e.exports=function(e,t){for(var n=i(t),u=a.f,s=o.f,l=0;l=74)&&(r=a.match(/Chrome\/(\d+)/))&&(i=r[1]),e.exports=i&&+i},748:function(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(e,t,n){var r=n(7854),i=n(1236).f,o=n(8880),a=n(1320),u=n(3505),s=n(9920),l=n(4705);e.exports=function(e,t){var n,c,f,p,h,d=e.target,v=e.global,y=e.stat;if(n=v?r:y?r[d]||u(d,{}):(r[d]||{}).prototype)for(c in t){if(p=t[c],f=e.noTargetGet?(h=i(n,c))&&h.value:n[c],!l(v?c:d+(y?".":"#")+c,e.forced)&&void 0!==f){if(typeof p==typeof f)continue;s(p,f)}(e.sham||f&&f.sham)&&o(p,"sham",!0),a(n,c,p,e)}}},7293:function(e){e.exports=function(e){try{return!!e()}catch(e){return!0}}},7007:function(e,t,n){"use strict";n(4916);var r=n(1320),i=n(7293),o=n(5112),a=n(2261),u=n(8880),s=o("species"),l=!i((function(){var e=/./;return e.exec=function(){var e=[];return e.groups={a:"7"},e},"7"!=="".replace(e,"$")})),c="$0"==="a".replace(/./,"$0"),f=o("replace"),p=!!/./[f]&&""===/./[f]("a","$0"),h=!i((function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2!==n.length||"a"!==n[0]||"b"!==n[1]}));e.exports=function(e,t,n,f){var d=o(e),v=!i((function(){var t={};return t[d]=function(){return 7},7!=""[e](t)})),y=v&&!i((function(){var t=!1,n=/a/;return"split"===e&&((n={}).constructor={},n.constructor[s]=function(){return n},n.flags="",n[d]=/./[d]),n.exec=function(){return t=!0,null},n[d](""),!t}));if(!v||!y||"replace"===e&&(!l||!c||p)||"split"===e&&!h){var g=/./[d],m=n(d,""[e],(function(e,t,n,r,i){return t.exec===a?v&&!i?{done:!0,value:g.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}}),{REPLACE_KEEPS_$0:c,REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE:p}),b=m[0],x=m[1];r(String.prototype,e,b),r(RegExp.prototype,d,2==t?function(e,t){return x.call(e,this,t)}:function(e){return x.call(e,this)})}f&&u(RegExp.prototype[d],"sham",!0)}},9974:function(e,t,n){var r=n(3099);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},5005:function(e,t,n){var r=n(857),i=n(7854),o=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?o(r[e])||o(i[e]):r[e]&&r[e][t]||i[e]&&i[e][t]}},1246:function(e,t,n){var r=n(648),i=n(7497),o=n(5112)("iterator");e.exports=function(e){if(null!=e)return e[o]||e["@@iterator"]||i[r(e)]}},8554:function(e,t,n){var r=n(9670),i=n(1246);e.exports=function(e){var t=i(e);if("function"!=typeof t)throw TypeError(String(e)+" is not iterable");return r(t.call(e))}},647:function(e,t,n){var r=n(7908),i=Math.floor,o="".replace,a=/\$([$&'`]|\d\d?|<[^>]*>)/g,u=/\$([$&'`]|\d\d?)/g;e.exports=function(e,t,n,s,l,c){var f=n+e.length,p=s.length,h=u;return void 0!==l&&(l=r(l),h=a),o.call(c,h,(function(r,o){var a;switch(o.charAt(0)){case"$":return"$";case"&":return e;case"`":return t.slice(0,n);case"'":return t.slice(f);case"<":a=l[o.slice(1,-1)];break;default:var u=+o;if(0===u)return r;if(u>p){var c=i(u/10);return 0===c?r:c<=p?void 0===s[c-1]?o.charAt(1):s[c-1]+o.charAt(1):r}a=s[u-1]}return void 0===a?"":a}))}},7854:function(e,t,n){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},6656:function(e){var t={}.hasOwnProperty;e.exports=function(e,n){return t.call(e,n)}},3501:function(e){e.exports={}},490:function(e,t,n){var r=n(5005);e.exports=r("document","documentElement")},4664:function(e,t,n){var r=n(9781),i=n(7293),o=n(317);e.exports=!r&&!i((function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}))},1179:function(e){var t=Math.abs,n=Math.pow,r=Math.floor,i=Math.log,o=Math.LN2;e.exports={pack:function(e,a,u){var s,l,c,f=new Array(u),p=8*u-a-1,h=(1<>1,v=23===a?n(2,-24)-n(2,-77):0,y=e<0||0===e&&1/e<0?1:0,g=0;for((e=t(e))!=e||e===1/0?(l=e!=e?1:0,s=h):(s=r(i(e)/o),e*(c=n(2,-s))<1&&(s--,c*=2),(e+=s+d>=1?v/c:v*n(2,1-d))*c>=2&&(s++,c/=2),s+d>=h?(l=0,s=h):s+d>=1?(l=(e*c-1)*n(2,a),s+=d):(l=e*n(2,d-1)*n(2,a),s=0));a>=8;f[g++]=255&l,l/=256,a-=8);for(s=s<0;f[g++]=255&s,s/=256,p-=8);return f[--g]|=128*y,f},unpack:function(e,t){var r,i=e.length,o=8*i-t-1,a=(1<>1,s=o-7,l=i-1,c=e[l--],f=127&c;for(c>>=7;s>0;f=256*f+e[l],l--,s-=8);for(r=f&(1<<-s)-1,f>>=-s,s+=t;s>0;r=256*r+e[l],l--,s-=8);if(0===f)f=1-u;else{if(f===a)return r?NaN:c?-1/0:1/0;r+=n(2,t),f-=u}return(c?-1:1)*r*n(2,f-t)}}},8361:function(e,t,n){var r=n(7293),i=n(4326),o="".split;e.exports=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(e){return"String"==i(e)?o.call(e,""):Object(e)}:Object},9587:function(e,t,n){var r=n(111),i=n(7674);e.exports=function(e,t,n){var o,a;return i&&"function"==typeof(o=t.constructor)&&o!==n&&r(a=o.prototype)&&a!==n.prototype&&i(e,a),e}},2788:function(e,t,n){var r=n(5465),i=Function.toString;"function"!=typeof r.inspectSource&&(r.inspectSource=function(e){return i.call(e)}),e.exports=r.inspectSource},9909:function(e,t,n){var r,i,o,a=n(8536),u=n(7854),s=n(111),l=n(8880),c=n(6656),f=n(5465),p=n(6200),h=n(3501),d=u.WeakMap;if(a){var v=f.state||(f.state=new d),y=v.get,g=v.has,m=v.set;r=function(e,t){return t.facade=e,m.call(v,e,t),t},i=function(e){return y.call(v,e)||{}},o=function(e){return g.call(v,e)}}else{var b=p("state");h[b]=!0,r=function(e,t){return t.facade=e,l(e,b,t),t},i=function(e){return c(e,b)?e[b]:{}},o=function(e){return c(e,b)}}e.exports={set:r,get:i,has:o,enforce:function(e){return o(e)?i(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!s(t)||(n=i(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return n}}}},7659:function(e,t,n){var r=n(5112),i=n(7497),o=r("iterator"),a=Array.prototype;e.exports=function(e){return void 0!==e&&(i.Array===e||a[o]===e)}},3157:function(e,t,n){var r=n(4326);e.exports=Array.isArray||function(e){return"Array"==r(e)}},4705:function(e,t,n){var r=n(7293),i=/#|\.prototype\./,o=function(e,t){var n=u[a(e)];return n==l||n!=s&&("function"==typeof t?r(t):!!t)},a=o.normalize=function(e){return String(e).replace(i,".").toLowerCase()},u=o.data={},s=o.NATIVE="N",l=o.POLYFILL="P";e.exports=o},111:function(e){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},1913:function(e){e.exports=!1},7850:function(e,t,n){var r=n(111),i=n(4326),o=n(5112)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[o])?!!t:"RegExp"==i(e))}},9212:function(e,t,n){var r=n(9670);e.exports=function(e){var t=e.return;if(void 0!==t)return r(t.call(e)).value}},3383:function(e,t,n){"use strict";var r,i,o,a=n(7293),u=n(9518),s=n(8880),l=n(6656),c=n(5112),f=n(1913),p=c("iterator"),h=!1;[].keys&&("next"in(o=[].keys())?(i=u(u(o)))!==Object.prototype&&(r=i):h=!0);var d=null==r||a((function(){var e={};return r[p].call(e)!==e}));d&&(r={}),f&&!d||l(r,p)||s(r,p,(function(){return this})),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:h}},7497:function(e){e.exports={}},133:function(e,t,n){var r=n(7293);e.exports=!!Object.getOwnPropertySymbols&&!r((function(){return!String(Symbol())}))},590:function(e,t,n){var r=n(7293),i=n(5112),o=n(1913),a=i("iterator");e.exports=!r((function(){var e=new URL("b?a=1&b=2&c=3","http://a"),t=e.searchParams,n="";return e.pathname="c%20d",t.forEach((function(e,r){t.delete("b"),n+=r+e})),o&&!e.toJSON||!t.sort||"http://a/c%20d?a=1&c=3"!==e.href||"3"!==t.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!t[a]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==n||"x"!==new URL("http://x",void 0).host}))},8536:function(e,t,n){var r=n(7854),i=n(2788),o=r.WeakMap;e.exports="function"==typeof o&&/native code/.test(i(o))},1574:function(e,t,n){"use strict";var r=n(9781),i=n(7293),o=n(1956),a=n(5181),u=n(5296),s=n(7908),l=n(8361),c=Object.assign,f=Object.defineProperty;e.exports=!c||i((function(){if(r&&1!==c({b:1},c(f({},"a",{enumerable:!0,get:function(){f(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var e={},t={},n=Symbol(),i="abcdefghijklmnopqrst";return e[n]=7,i.split("").forEach((function(e){t[e]=e})),7!=c({},e)[n]||o(c({},t)).join("")!=i}))?function(e,t){for(var n=s(e),i=arguments.length,c=1,f=a.f,p=u.f;i>c;)for(var h,d=l(arguments[c++]),v=f?o(d).concat(f(d)):o(d),y=v.length,g=0;y>g;)h=v[g++],r&&!p.call(d,h)||(n[h]=d[h]);return n}:c},30:function(e,t,n){var r,i=n(9670),o=n(6048),a=n(748),u=n(3501),s=n(490),l=n(317),c=n(6200)("IE_PROTO"),f=function(){},p=function(e){return" diff --git a/static/style.css b/static/style.css index c18282b..a603372 100644 --- a/static/style.css +++ b/static/style.css @@ -394,13 +394,17 @@ body { background: white; border-radius: 12px; width: 100%; - max-width: 450px; + max-width: 600px; max-height: 90vh; overflow: hidden; display: flex; flex-direction: column; } +.upload-modal .modal-content { + max-width: 650px; +} + .modal-large { max-width: 900px; } @@ -777,3 +781,177 @@ body { .upload-speed.active { display: block; } + +.upload-file-list { + margin-top: 15px; + max-height: 250px; + overflow-y: auto; +} + +.upload-file-item { + display: flex; + align-items: center; + padding: 8px 10px; + border: 1px solid #eee; + border-radius: 6px; + margin-bottom: 6px; + background: #fafafa; + gap: 10px; +} + +.upload-file-item:last-child { + margin-bottom: 0; +} + +.upload-file-item .dz-icon { + font-size: 20px; + width: 24px; + text-align: center; + flex-shrink: 0; +} + +.upload-file-item .dz-info { + flex: 1; + min-width: 0; + overflow: hidden; +} + +.upload-file-item .dz-filename { + font-size: 12px; + color: #333; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 2px; +} + +.upload-file-item .dz-size { + font-size: 10px; + color: #999; +} + +.upload-file-item .dz-progress-wrap { + width: 100px; + flex-shrink: 0; + display: flex; + align-items: center; +} + +.upload-file-item .dz-percent { + font-size: 11px; + color: #666; + margin-left: 6px; + min-width: 36px; + text-align: left; + font-family: monospace; +} + +.upload-file-item .dz-chunks { + font-size: 10px; + color: #999; + margin-right: 4px; +} + +.upload-file-item .dz-progress { + width: 100%; + height: 6px; + background: #e0e0e0; + border-radius: 3px; + overflow: hidden; + display: block; + margin: 0; + padding: 0; +} + +.upload-file-item .dz-upload { + display: block; + height: 100%; + background: #2196f3 !important; + border-radius: 3px; + width: 0%; + transition: width 0.1s linear; + min-width: 0; +} + +.upload-file-item .dz-status { + width: 24px; + text-align: center; + font-size: 16px; + flex-shrink: 0; +} + +.upload-file-item .dz-success-mark { + color: #4caf50; + display: none; +} + +.upload-file-item .dz-error-mark { + color: #f44336; + display: none; +} + +.upload-file-item.dz-success .dz-success-mark { + display: inline; +} + +.upload-file-item.dz-error .dz-error-mark { + display: inline; +} + +.upload-file-item .dz-remove { + color: #999; + font-size: 14px; + cursor: pointer; + padding: 4px 6px; + border-radius: 4px; + flex-shrink: 0; +} + +.upload-file-item .dz-remove:hover { + background: #ffebee; + color: #f44336; +} + +.upload-file-item .dz-error-message { + color: #f44336; + font-size: 11px; + padding: 4px 8px; + background: #ffebee; + border-radius: 4px; + margin-top: 4px; + display: none; +} + +.upload-file-item.dz-error .dz-error-message { + display: block; +} + +.upload-file-item.dz-error .dz-filename { + color: #f44336; +} + +.dropzone { + border: 2px dashed #e0e0e0 !important; + border-radius: 8px !important; + background: #fafafa !important; + min-height: 100px !important; + padding: 20px !important; + transition: all 0.2s; +} + +.dropzone:hover, +.dropzone.dz-drag-hover { + border-color: #2196f3 !important; + background: #f0f7ff !important; +} + +.dropzone .dz-message { + text-align: center; + color: #999; + font-size: 14px; + margin: 20px 0; +} + +.dropzone .dz-preview { + display: none !important; +}