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:
@@ -44,18 +44,29 @@ body {
|
|||||||
min-height: 44px;
|
min-height: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 上传进度条 */
|
/* 传输进度(上传/下载/跨面板传输) */
|
||||||
.upload-progress {
|
.transfer-progress {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-progress .progress {
|
.transfer-progress-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333;
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-progress .progress {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-progress .progress-bar {
|
.transfer-progress .progress-bar {
|
||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|||||||
@@ -33,10 +33,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 文件上传输入框(隐藏) -->
|
<!-- 文件上传输入框(隐藏) -->
|
||||||
<input type="file" id="file-input" multiple style="display:none">
|
<input type="file" id="file-input" multiple style="display:none">
|
||||||
<!-- 上传进度条 -->
|
<!-- 传输进度(上传/下载/跨面板传输) -->
|
||||||
<div class="upload-progress" id="upload-progress" style="display:none;">
|
<div class="transfer-progress" id="transfer-progress" style="display:none;">
|
||||||
<div class="progress" style="height: 20px; margin-left: 10px; width: 200px;">
|
<span class="transfer-progress-label" id="transfer-progress-label"></span>
|
||||||
<div class="progress-bar" role="progressbar" style="width: 0%">0%</div>
|
<div class="progress transfer-progress-bar" style="height: 20px; margin-left: 10px; width: 220px;">
|
||||||
|
<div class="progress-bar" id="transfer-progress-bar" role="progressbar" style="width: 0%">0%</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 为数组时渲染,避免错误数据结构被当成文件显示)
|
// 渲染文件列表(仅当 data 为数组时渲染,避免错误数据结构被当成文件显示)
|
||||||
function renderFileList(panelId, files) {
|
function renderFileList(panelId, files) {
|
||||||
const fileList = $(`#${panelId}-file-list`);
|
const fileList = $(`#${panelId}-file-list`);
|
||||||
@@ -131,6 +144,8 @@ function renderFileList(panelId, files) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
files = sortFileList(files);
|
||||||
|
|
||||||
files.forEach(function(file) {
|
files.forEach(function(file) {
|
||||||
// 确保 isDirectory 字段存在且为布尔值
|
// 确保 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) {
|
function handleFileDrop(data, targetPanelId) {
|
||||||
const sourcePanelId = data.panelId;
|
const sourcePanelId = data.panelId;
|
||||||
@@ -335,6 +360,10 @@ function handleFileDrop(data, targetPanelId) {
|
|||||||
|
|
||||||
const sourceSessionId = panelState[sourcePanelId].sessionId;
|
const sourceSessionId = panelState[sourcePanelId].sessionId;
|
||||||
const targetSessionId = panelState[targetPanelId].sessionId;
|
const targetSessionId = panelState[targetPanelId].sessionId;
|
||||||
|
const fileName = getFileNameFromPath(sourcePath);
|
||||||
|
|
||||||
|
showTransferCountProgress(0, 1, fileName);
|
||||||
|
updateTransferProgress(0, '传输中 ' + fileName);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: API_BASE + 'api/files/transfer',
|
url: API_BASE + 'api/files/transfer',
|
||||||
@@ -347,6 +376,7 @@ function handleFileDrop(data, targetPanelId) {
|
|||||||
targetPath: targetPath
|
targetPath: targetPath
|
||||||
}),
|
}),
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
showTransferProgress(false);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
updateStatus('传输成功');
|
updateStatus('传输成功');
|
||||||
loadFiles(targetPanelId);
|
loadFiles(targetPanelId);
|
||||||
@@ -355,6 +385,7 @@ function handleFileDrop(data, targetPanelId) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
|
showTransferProgress(false);
|
||||||
const errMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : error;
|
const errMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : error;
|
||||||
alert('传输失败: ' + errMsg);
|
alert('传输失败: ' + errMsg);
|
||||||
}
|
}
|
||||||
@@ -897,7 +928,8 @@ function uploadSingleFile(file, targetSessionId, targetPath, targetPanelId) {
|
|||||||
formData.append('targetSessionId', targetSessionId);
|
formData.append('targetSessionId', targetSessionId);
|
||||||
formData.append('targetPath', targetPath);
|
formData.append('targetPath', targetPath);
|
||||||
|
|
||||||
showUploadProgress(true);
|
showTransferProgress(true, '上传: ' + file.name + ' 0%');
|
||||||
|
updateTransferProgress(0, '上传: ' + file.name + ' 0%');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: API_BASE + 'api/files/upload',
|
url: API_BASE + 'api/files/upload',
|
||||||
@@ -910,7 +942,7 @@ function uploadSingleFile(file, targetSessionId, targetPath, targetPanelId) {
|
|||||||
xhr.upload.addEventListener('progress', function(e) {
|
xhr.upload.addEventListener('progress', function(e) {
|
||||||
if (e.lengthComputable) {
|
if (e.lengthComputable) {
|
||||||
const percent = Math.round((e.loaded / e.total) * 100);
|
const percent = Math.round((e.loaded / e.total) * 100);
|
||||||
updateUploadProgress(percent);
|
updateTransferProgress(percent, '上传: ' + file.name + ' ' + percent + '%');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return xhr;
|
return xhr;
|
||||||
@@ -928,30 +960,89 @@ function uploadSingleFile(file, targetSessionId, targetPath, targetPanelId) {
|
|||||||
alert('上传失败: ' + errMsg);
|
alert('上传失败: ' + errMsg);
|
||||||
},
|
},
|
||||||
complete: function() {
|
complete: function() {
|
||||||
showUploadProgress(false);
|
showTransferProgress(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示上传进度
|
// 显示/隐藏传输进度区域(上传、下载、跨面板传输共用)
|
||||||
function showUploadProgress(show) {
|
function showTransferProgress(show, label) {
|
||||||
const progressDiv = $('#upload-progress');
|
const progressDiv = $('#transfer-progress');
|
||||||
progressDiv.css('display', show ? 'inline-block' : 'none');
|
progressDiv.css('display', show ? 'inline-flex' : 'none');
|
||||||
|
if (label !== undefined) {
|
||||||
|
$('#transfer-progress-label').text(label || '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新上传进度
|
// 更新传输进度条与标签
|
||||||
function updateUploadProgress(percent) {
|
function updateTransferProgress(percent, label) {
|
||||||
const progressBar = $('#upload-progress .progress-bar');
|
const progressBar = $('#transfer-progress-bar');
|
||||||
progressBar.css('width', percent + '%');
|
progressBar.css('width', percent + '%');
|
||||||
progressBar.text(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) {
|
function downloadFile(sessionId, path) {
|
||||||
const url = API_BASE + 'api/files/download?sessionId=' +
|
const url = API_BASE + 'api/files/download?sessionId=' +
|
||||||
encodeURIComponent(sessionId) +
|
encodeURIComponent(sessionId) +
|
||||||
'&path=' + encodeURIComponent(path);
|
'&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;
|
let failed = 0;
|
||||||
const total = selectedFiles.length;
|
const total = selectedFiles.length;
|
||||||
|
|
||||||
selectedFiles.forEach(function(sourcePath) {
|
showTransferCountProgress(0, total, '');
|
||||||
|
updateTransferProgress(0, '传输中 (0/' + total + ')');
|
||||||
|
|
||||||
|
selectedFiles.forEach(function(sourcePath, index) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: API_BASE + 'api/files/transfer',
|
url: API_BASE + 'api/files/transfer',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -1009,36 +1103,40 @@ function transferFiles() {
|
|||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
completed++;
|
completed++;
|
||||||
if (completed + failed === total) {
|
|
||||||
if (failed === 0) {
|
|
||||||
updateStatus('所有文件传输成功');
|
|
||||||
} else {
|
|
||||||
updateStatus(`传输完成:成功 ${completed},失败 ${failed}`);
|
|
||||||
}
|
|
||||||
loadFiles(targetPanelId);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
failed++;
|
failed++;
|
||||||
alert('传输失败: ' + (response.message || '未知错误'));
|
alert('传输失败: ' + (response.message || '未知错误'));
|
||||||
if (completed + failed === total) {
|
}
|
||||||
updateStatus(`传输完成:成功 ${completed},失败 ${failed}`);
|
const done = completed + failed;
|
||||||
loadFiles(targetPanelId);
|
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) {
|
error: function(xhr, status, error) {
|
||||||
failed++;
|
failed++;
|
||||||
const errMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : error;
|
const errMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : error;
|
||||||
alert('传输失败: ' + errMsg);
|
alert('传输失败: ' + errMsg);
|
||||||
if (completed + failed === total) {
|
const done = completed + failed;
|
||||||
updateStatus(`传输完成:成功 ${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);
|
loadFiles(targetPanelId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
updateStatus(`正在传输 ${total} 个文件...`);
|
updateStatus('正在传输 ' + total + ' 个文件...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 文件删除功能(模块06)==========
|
// ========== 文件删除功能(模块06)==========
|
||||||
|
|||||||
Reference in New Issue
Block a user