Refactor file transfer progress handling in app.js and update UI elements in index.html and style.css. Introduced a unified transfer progress display for uploads, downloads, and cross-panel transfers, enhancing user experience and visual feedback.

This commit is contained in:
liu
2026-02-03 10:35:59 +08:00
parent 9f72ff5a48
commit 96f78dea87
3 changed files with 146 additions and 36 deletions

View File

@@ -117,6 +117,19 @@ function loadFiles(panelId) {
});
}
// 排序文件列表:文件夹在前,同类型按名称(不区分大小写)
function sortFileList(files) {
if (!Array.isArray(files) || files.length === 0) return files;
return files.slice().sort(function(a, b) {
const aDir = a.isDirectory === true || a.isDirectory === 'true' || a.directory === true || a.directory === 'true';
const bDir = b.isDirectory === true || b.isDirectory === 'true' || b.directory === true || b.directory === 'true';
if (aDir !== bDir) return aDir ? -1 : 1;
const nameA = (a.name || '').toLowerCase();
const nameB = (b.name || '').toLowerCase();
return nameA.localeCompare(nameB, 'zh-CN', { sensitivity: 'base' });
});
}
// 渲染文件列表(仅当 data 为数组时渲染,避免错误数据结构被当成文件显示)
function renderFileList(panelId, files) {
const fileList = $(`#${panelId}-file-list`);
@@ -131,6 +144,8 @@ function renderFileList(panelId, files) {
return;
}
files = sortFileList(files);
files.forEach(function(file) {
// 确保 isDirectory 字段存在且为布尔值
// 兼容多种可能的字段名和值类型
@@ -322,6 +337,16 @@ function initDragAndDrop() {
});
}
// 显示跨面板传输进度(按文件个数)
function showTransferCountProgress(current, total, fileName) {
const label = total > 1
? '传输中 (' + current + '/' + total + ')' + (fileName ? ' ' + fileName : '')
: '传输中 ' + (fileName || '');
const percent = total > 0 ? Math.round((current / total) * 100) : 0;
showTransferProgress(true, label);
updateTransferProgress(percent, label);
}
// 处理文件拖放(同面板移动 / 跨面板传输)
function handleFileDrop(data, targetPanelId) {
const sourcePanelId = data.panelId;
@@ -335,6 +360,10 @@ function handleFileDrop(data, targetPanelId) {
const sourceSessionId = panelState[sourcePanelId].sessionId;
const targetSessionId = panelState[targetPanelId].sessionId;
const fileName = getFileNameFromPath(sourcePath);
showTransferCountProgress(0, 1, fileName);
updateTransferProgress(0, '传输中 ' + fileName);
$.ajax({
url: API_BASE + 'api/files/transfer',
@@ -347,6 +376,7 @@ function handleFileDrop(data, targetPanelId) {
targetPath: targetPath
}),
success: function(response) {
showTransferProgress(false);
if (response.success) {
updateStatus('传输成功');
loadFiles(targetPanelId);
@@ -355,6 +385,7 @@ function handleFileDrop(data, targetPanelId) {
}
},
error: function(xhr, status, error) {
showTransferProgress(false);
const errMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : error;
alert('传输失败: ' + errMsg);
}
@@ -897,7 +928,8 @@ function uploadSingleFile(file, targetSessionId, targetPath, targetPanelId) {
formData.append('targetSessionId', targetSessionId);
formData.append('targetPath', targetPath);
showUploadProgress(true);
showTransferProgress(true, '上传: ' + file.name + ' 0%');
updateTransferProgress(0, '上传: ' + file.name + ' 0%');
$.ajax({
url: API_BASE + 'api/files/upload',
@@ -910,7 +942,7 @@ function uploadSingleFile(file, targetSessionId, targetPath, targetPanelId) {
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
updateUploadProgress(percent);
updateTransferProgress(percent, '上传: ' + file.name + ' ' + percent + '%');
}
});
return xhr;
@@ -928,30 +960,89 @@ function uploadSingleFile(file, targetSessionId, targetPath, targetPanelId) {
alert('上传失败: ' + errMsg);
},
complete: function() {
showUploadProgress(false);
showTransferProgress(false);
}
});
}
// 显示上传进度
function showUploadProgress(show) {
const progressDiv = $('#upload-progress');
progressDiv.css('display', show ? 'inline-block' : 'none');
// 显示/隐藏传输进度区域(上传、下载、跨面板传输共用)
function showTransferProgress(show, label) {
const progressDiv = $('#transfer-progress');
progressDiv.css('display', show ? 'inline-flex' : 'none');
if (label !== undefined) {
$('#transfer-progress-label').text(label || '');
}
}
// 更新上传进度
function updateUploadProgress(percent) {
const progressBar = $('#upload-progress .progress-bar');
// 更新传输进度条与标签
function updateTransferProgress(percent, label) {
const progressBar = $('#transfer-progress-bar');
progressBar.css('width', percent + '%');
progressBar.text(percent + '%');
if (label !== undefined) {
$('#transfer-progress-label').text(label);
}
}
// 下载文件
// 显示上传进度(兼容旧调用)
function showUploadProgress(show) {
showTransferProgress(show);
}
// 更新上传进度(带文件名)
function updateUploadProgress(percent, fileName) {
const label = fileName ? '上传: ' + fileName + ' ' + percent + '%' : percent + '%';
updateTransferProgress(percent, label);
}
// 下载文件(带进度,使用 XHR
function downloadFile(sessionId, path) {
const url = API_BASE + 'api/files/download?sessionId=' +
encodeURIComponent(sessionId) +
'&path=' + encodeURIComponent(path);
window.location.href = url;
const fileName = getFileNameFromPath(path) || '下载';
showTransferProgress(true, '下载: ' + fileName + ' 0%');
updateTransferProgress(0, '下载: ' + fileName + ' 0%');
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.addEventListener('progress', function(e) {
if (e.lengthComputable && e.total > 0) {
const percent = Math.round((e.loaded / e.total) * 100);
updateTransferProgress(percent, '下载: ' + fileName + ' ' + percent + '%');
} else if (e.loaded > 0) {
const kb = (e.loaded / 1024).toFixed(1);
updateTransferProgress(0, '下载: ' + fileName + ' 已接收 ' + kb + ' KB');
}
});
xhr.addEventListener('load', function() {
showTransferProgress(false);
if (xhr.status !== 200) {
alert('下载失败: ' + (xhr.statusText || 'HTTP ' + xhr.status));
return;
}
const blob = new Blob([xhr.response]);
const blobUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(blobUrl);
updateStatus(fileName + ' 下载完成');
});
xhr.addEventListener('error', function() {
showTransferProgress(false);
alert('下载失败: 网络错误');
});
xhr.send();
}
// 下载选中的文件
@@ -995,7 +1086,10 @@ function transferFiles() {
let failed = 0;
const total = selectedFiles.length;
selectedFiles.forEach(function(sourcePath) {
showTransferCountProgress(0, total, '');
updateTransferProgress(0, '传输中 (0/' + total + ')');
selectedFiles.forEach(function(sourcePath, index) {
$.ajax({
url: API_BASE + 'api/files/transfer',
method: 'POST',
@@ -1009,36 +1103,40 @@ function transferFiles() {
success: function(response) {
if (response.success) {
completed++;
if (completed + failed === total) {
if (failed === 0) {
updateStatus('所有文件传输成功');
} else {
updateStatus(`传输完成:成功 ${completed},失败 ${failed}`);
}
loadFiles(targetPanelId);
}
} else {
failed++;
alert('传输失败: ' + (response.message || '未知错误'));
if (completed + failed === total) {
updateStatus(`传输完成:成功 ${completed},失败 ${failed}`);
loadFiles(targetPanelId);
}
const done = completed + failed;
showTransferCountProgress(done, total, getFileNameFromPath(sourcePath));
updateTransferProgress(total > 0 ? Math.round((done / total) * 100) : 0, '传输中 (' + done + '/' + total + ')');
if (done === total) {
showTransferProgress(false);
if (failed === 0) {
updateStatus('所有文件传输成功');
} else {
updateStatus('传输完成:成功 ' + completed + ',失败 ' + failed);
}
loadFiles(targetPanelId);
}
},
error: function(xhr, status, error) {
failed++;
const errMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : error;
alert('传输失败: ' + errMsg);
if (completed + failed === total) {
updateStatus(`传输完成:成功 ${completed},失败 ${failed}`);
const done = completed + failed;
showTransferCountProgress(done, total, getFileNameFromPath(sourcePath));
updateTransferProgress(total > 0 ? Math.round((done / total) * 100) : 0, '传输中 (' + done + '/' + total + ')');
if (done === total) {
showTransferProgress(false);
updateStatus('传输完成:成功 ' + completed + ',失败 ' + failed);
loadFiles(targetPanelId);
}
}
});
});
updateStatus(`正在传输 ${total} 个文件...`);
updateStatus('正在传输 ' + total + ' 个文件...');
}
// ========== 文件删除功能模块06==========