feat(ui): добавляем анимированный прогресс-бар при обновлении сборки
- обновили updateInstance() с поддержкой SSE для реального прогресса - добавили showAnimatedProgress() и updateAnimatedProgress() методы - добавили CSS анимацию shimmer для прогресс-бара - теперь показывает: файл X из Y и процент текущего файла
This commit is contained in:
@@ -686,6 +686,30 @@ body {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.progress-file {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.progress-fill.animated {
|
||||
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary), var(--accent-primary));
|
||||
background-size: 200% 100%;
|
||||
animation: progressShimmer 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes progressShimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* ==================== LOADING ==================== */
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
|
||||
@@ -325,7 +325,43 @@ class App {
|
||||
}
|
||||
|
||||
this.addLog('Обновление сборки...', 'info');
|
||||
this.showProgress('Обновление сборки...');
|
||||
this.enablePlayButton(false);
|
||||
|
||||
const progressContainer = this.showAnimatedProgress('Обновление сборки...');
|
||||
|
||||
let eventSource = null;
|
||||
let progressData = { totalFiles: 0, downloadedFiles: 0 };
|
||||
|
||||
try {
|
||||
eventSource = new EventSource(`/api/instances/${this.currentInstance.name}/install/stream`);
|
||||
eventSource.onmessage = (e) => {
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data.phase === 'starting') {
|
||||
progressData.totalFiles = data.totalFiles || 0;
|
||||
this.updateAnimatedProgress(progressContainer, `Загрузка: 0/${progressData.totalFiles} файлов`, 5);
|
||||
} else if (data.phase === 'downloading') {
|
||||
progressData.downloadedFiles = data.downloadedFiles || 0;
|
||||
const total = data.totalFiles || progressData.totalFiles || 1;
|
||||
const percent = Math.round((progressData.downloadedFiles / total) * 100);
|
||||
const fileName = data.currentFile ? data.currentFile.split('/').pop() : '';
|
||||
const filePercent = data.filePercent || 0;
|
||||
this.updateAnimatedProgress(progressContainer,
|
||||
`Файл ${progressData.downloadedFiles}/${total} (${percent}%)`,
|
||||
percent,
|
||||
fileName,
|
||||
filePercent
|
||||
);
|
||||
} else if (data.phase === 'complete') {
|
||||
this.updateAnimatedProgress(progressContainer, 'Готово!', 100);
|
||||
} else if (data.phase === 'error') {
|
||||
this.addLog('Ошибка: ' + (data.message || 'неизвестная ошибка'), 'error');
|
||||
}
|
||||
} catch (err) {}
|
||||
};
|
||||
} catch (e) {
|
||||
console.log('SSE not available, using fallback progress');
|
||||
}
|
||||
|
||||
const result = await this.request('/instances/zernmc/install', {
|
||||
method: 'POST',
|
||||
@@ -335,6 +371,10 @@ class App {
|
||||
})
|
||||
});
|
||||
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
}
|
||||
|
||||
this.hideProgress();
|
||||
|
||||
if (result.success) {
|
||||
@@ -362,6 +402,31 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
showAnimatedProgress(text) {
|
||||
const progress = document.getElementById('download-progress');
|
||||
const progressText = document.getElementById('progress-text');
|
||||
const progressFill = document.getElementById('progress-fill');
|
||||
|
||||
progress.classList.remove('hidden');
|
||||
progressText.innerHTML = `<div class="progress-label">${text}</div>
|
||||
<div class="progress-file"></div>`;
|
||||
progressFill.style.width = '5%';
|
||||
progressFill.classList.add('animated');
|
||||
|
||||
return { container: progress, text: progressText, fill: progressFill };
|
||||
}
|
||||
|
||||
updateAnimatedProgress(progressContainer, text, percent, fileName = '', filePercent = 0) {
|
||||
const { text: progressText, fill: progressFill } = progressContainer;
|
||||
if (fileName) {
|
||||
progressText.innerHTML = `<div class="progress-label">${text}</div>
|
||||
<div class="progress-file">${fileName} (${filePercent}%)</div>`;
|
||||
} else {
|
||||
progressText.innerHTML = `<div class="progress-label">${text}</div>`;
|
||||
}
|
||||
progressFill.style.width = percent + '%';
|
||||
}
|
||||
|
||||
// ==================== DOWNLOAD MODAL ====================
|
||||
async showDownloadModal() {
|
||||
document.getElementById('download-modal').classList.remove('hidden');
|
||||
|
||||
Reference in New Issue
Block a user