428 lines
14 KiB
JavaScript
428 lines
14 KiB
JavaScript
// files.js - Работа с файлами
|
||
let currentTaskFiles = [];
|
||
let currentEditTaskFiles = [];
|
||
|
||
function initializeFileUploads() {
|
||
// Создание задачи
|
||
document.getElementById('files').addEventListener('change', function(e) {
|
||
currentTaskFiles = Array.from(e.target.files);
|
||
updateFileList();
|
||
});
|
||
|
||
// Редактирование задачи
|
||
document.getElementById('edit-files').addEventListener('change', function(e) {
|
||
const newFiles = Array.from(e.target.files);
|
||
currentEditTaskFiles.push(...newFiles);
|
||
updateEditFileList();
|
||
});
|
||
}
|
||
|
||
function updateFileList() {
|
||
const fileInput = document.getElementById('files');
|
||
const fileList = document.getElementById('file-list');
|
||
updateFileListForInput(fileInput, fileList);
|
||
}
|
||
|
||
function updateEditFileList() {
|
||
const fileInput = document.getElementById('edit-files');
|
||
const fileList = document.getElementById('edit-file-list');
|
||
|
||
// Используем улучшенный рендеринг файлов
|
||
const files = fileInput.files;
|
||
const existingFiles = currentEditTaskFiles.filter(file => !(file instanceof File));
|
||
|
||
if (files.length === 0 && existingFiles.length === 0) {
|
||
fileList.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
let html = '<ul>';
|
||
let totalSize = 0;
|
||
|
||
// Существующие файлы
|
||
existingFiles.forEach(file => {
|
||
totalSize += file.file_size;
|
||
html += `<li>${file.original_name} (${(file.file_size / 1024 / 1024).toFixed(2)} MB) - <em>уже загружен</em></li>`;
|
||
});
|
||
|
||
// Новые файлы
|
||
for (let i = 0; i < files.length; i++) {
|
||
const file = files[i];
|
||
totalSize += file.size;
|
||
html += `<li>${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB) - <em>новый</em></li>`;
|
||
}
|
||
|
||
html += '</ul>';
|
||
html += `<p><strong>Общий размер: ${(totalSize / 1024 / 1024).toFixed(2)} MB / 300 MB</strong></p>`;
|
||
|
||
fileList.innerHTML = html;
|
||
}
|
||
|
||
function updateFileListForInput(fileInput, fileList) {
|
||
const files = fileInput.files;
|
||
|
||
if (files.length === 0) {
|
||
fileList.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
let html = '<ul>';
|
||
let totalSize = 0;
|
||
|
||
for (let i = 0; i < files.length; i++) {
|
||
const file = files[i];
|
||
totalSize += file.size;
|
||
html += `<li>${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)</li>`;
|
||
}
|
||
|
||
html += '</ul>';
|
||
html += `<p><strong>Общий размер: ${(totalSize / 1024 / 1024).toFixed(2)} MB / 300 MB</strong></p>`;
|
||
|
||
fileList.innerHTML = html;
|
||
}
|
||
|
||
// Удаление файлов из списка
|
||
function removeFile(index) {
|
||
currentTaskFiles.splice(index, 1);
|
||
updateFileList();
|
||
|
||
// Обновляем input files
|
||
const dataTransfer = new DataTransfer();
|
||
currentTaskFiles.forEach(file => dataTransfer.items.add(file));
|
||
document.getElementById('files').files = dataTransfer.files;
|
||
}
|
||
|
||
function removeEditFile(index) {
|
||
currentEditTaskFiles.splice(index, 1);
|
||
updateEditFileList();
|
||
|
||
// Обновляем input files
|
||
const dataTransfer = new DataTransfer();
|
||
const newFiles = currentEditTaskFiles.filter(file => file instanceof File);
|
||
newFiles.forEach(file => dataTransfer.items.add(file));
|
||
document.getElementById('edit-files').files = dataTransfer.files;
|
||
}
|
||
|
||
function renderFileIcon(file) {
|
||
// Исправляем кодировку имени файла
|
||
const fixEncoding = (str) => {
|
||
if (!str) return '';
|
||
try {
|
||
// Пробуем разные способы декодирования
|
||
if (str.includes('Ð') || str.includes('Ñ')) {
|
||
// UTF-8 неправильно декодированный как Latin-1
|
||
return decodeURIComponent(escape(str));
|
||
}
|
||
return str;
|
||
} catch (e) {
|
||
return str;
|
||
}
|
||
};
|
||
|
||
const fileName = fixEncoding(file.original_name);
|
||
const fileSize = (file.file_size / 1024 / 1024).toFixed(2);
|
||
const uploadedBy = file.user_name;
|
||
|
||
let iconColor = '';
|
||
let iconText = '';
|
||
let textClass = '';
|
||
|
||
// Определяем расширение файла
|
||
const extension = fileName.includes('.') ?
|
||
fileName.split('.').pop().toLowerCase() :
|
||
'';
|
||
|
||
// Определяем тип файла на основе расширения
|
||
if (extension) {
|
||
switch (extension) {
|
||
case 'pdf':
|
||
iconColor = '#e74c3c';
|
||
iconText = 'PDF';
|
||
textClass = 'short';
|
||
break;
|
||
case 'doc':
|
||
iconColor = '#3498db';
|
||
iconText = 'DOC';
|
||
textClass = 'short';
|
||
break;
|
||
case 'docx':
|
||
iconColor = '#3498db';
|
||
iconText = 'DOCX';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'xls':
|
||
iconColor = '#2ecc71';
|
||
iconText = 'XLS';
|
||
textClass = 'short';
|
||
break;
|
||
case 'xlsx':
|
||
iconColor = '#2ecc71';
|
||
iconText = 'XLSX';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'csv':
|
||
iconColor = '#2ecc71';
|
||
iconText = 'CSV';
|
||
textClass = 'short';
|
||
break;
|
||
case 'ppt':
|
||
iconColor = '#e67e22';
|
||
iconText = 'PPT';
|
||
textClass = 'short';
|
||
break;
|
||
case 'pptx':
|
||
iconColor = '#e67e22';
|
||
iconText = 'PPTX';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'zip':
|
||
iconColor = '#f39c12';
|
||
iconText = 'ZIP';
|
||
textClass = 'short';
|
||
break;
|
||
case 'rar':
|
||
iconColor = '#f39c12';
|
||
iconText = 'RAR';
|
||
textClass = 'short';
|
||
break;
|
||
case '7z':
|
||
iconColor = '#f39c12';
|
||
iconText = '7Z';
|
||
textClass = 'short';
|
||
break;
|
||
case 'tar':
|
||
iconColor = '#f39c12';
|
||
iconText = 'TAR';
|
||
textClass = 'short';
|
||
break;
|
||
case 'gz':
|
||
iconColor = '#f39c12';
|
||
iconText = 'GZ';
|
||
textClass = 'short';
|
||
break;
|
||
case 'txt':
|
||
iconColor = '#95a5a6';
|
||
iconText = 'TXT';
|
||
textClass = 'short';
|
||
break;
|
||
case 'log':
|
||
iconColor = '#95a5a6';
|
||
iconText = 'LOG';
|
||
textClass = 'short';
|
||
break;
|
||
case 'md':
|
||
iconColor = '#95a5a6';
|
||
iconText = 'MD';
|
||
textClass = 'short';
|
||
break;
|
||
case 'jpg':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'JPG';
|
||
textClass = 'short';
|
||
break;
|
||
case 'jpeg':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'JPEG';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'png':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'PNG';
|
||
textClass = 'short';
|
||
break;
|
||
case 'gif':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'GIF';
|
||
textClass = 'short';
|
||
break;
|
||
case 'bmp':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'BMP';
|
||
textClass = 'short';
|
||
break;
|
||
case 'svg':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'SVG';
|
||
textClass = 'short';
|
||
break;
|
||
case 'webp':
|
||
iconColor = '#9b59b6';
|
||
iconText = 'WEBP';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'mp3':
|
||
iconColor = '#1abc9c';
|
||
iconText = 'MP3';
|
||
textClass = 'short';
|
||
break;
|
||
case 'wav':
|
||
iconColor = '#1abc9c';
|
||
iconText = 'WAV';
|
||
textClass = 'short';
|
||
break;
|
||
case 'ogg':
|
||
iconColor = '#1abc9c';
|
||
iconText = 'OGG';
|
||
textClass = 'short';
|
||
break;
|
||
case 'flac':
|
||
iconColor = '#1abc9c';
|
||
iconText = 'FLAC';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'mp4':
|
||
iconColor = '#d35400';
|
||
iconText = 'MP4';
|
||
textClass = 'short';
|
||
break;
|
||
case 'avi':
|
||
iconColor = '#d35400';
|
||
iconText = 'AVI';
|
||
textClass = 'short';
|
||
break;
|
||
case 'mkv':
|
||
iconColor = '#d35400';
|
||
iconText = 'MKV';
|
||
textClass = 'short';
|
||
break;
|
||
case 'mov':
|
||
iconColor = '#d35400';
|
||
iconText = 'MOV';
|
||
textClass = 'short';
|
||
break;
|
||
case 'wmv':
|
||
iconColor = '#d35400';
|
||
iconText = 'WMV';
|
||
textClass = 'short';
|
||
break;
|
||
case 'exe':
|
||
iconColor = '#c0392b';
|
||
iconText = 'EXE';
|
||
textClass = 'short';
|
||
break;
|
||
case 'msi':
|
||
iconColor = '#c0392b';
|
||
iconText = 'MSI';
|
||
textClass = 'short';
|
||
break;
|
||
case 'js':
|
||
iconColor = '#2980b9';
|
||
iconText = 'JS';
|
||
textClass = 'short';
|
||
break;
|
||
case 'html':
|
||
iconColor = '#2980b9';
|
||
iconText = 'HTML';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'css':
|
||
iconColor = '#2980b9';
|
||
iconText = 'CSS';
|
||
textClass = 'short';
|
||
break;
|
||
case 'php':
|
||
iconColor = '#2980b9';
|
||
iconText = 'PHP';
|
||
textClass = 'short';
|
||
break;
|
||
case 'py':
|
||
iconColor = '#2980b9';
|
||
iconText = 'PY';
|
||
textClass = 'short';
|
||
break;
|
||
case 'java':
|
||
iconColor = '#2980b9';
|
||
iconText = 'JAVA';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'json':
|
||
iconColor = '#8e44ad';
|
||
iconText = 'JSON';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'xml':
|
||
iconColor = '#8e44ad';
|
||
iconText = 'XML';
|
||
textClass = 'short';
|
||
break;
|
||
case 'yml':
|
||
iconColor = '#8e44ad';
|
||
iconText = 'YML';
|
||
textClass = 'short';
|
||
break;
|
||
case 'yaml':
|
||
iconColor = '#8e44ad';
|
||
iconText = 'YAML';
|
||
textClass = 'medium';
|
||
break;
|
||
case 'sql':
|
||
iconColor = '#27ae60';
|
||
iconText = 'SQL';
|
||
textClass = 'short';
|
||
break;
|
||
case 'db':
|
||
iconColor = '#27ae60';
|
||
iconText = 'DB';
|
||
textClass = 'short';
|
||
break;
|
||
case 'sqlite':
|
||
iconColor = '#27ae60';
|
||
iconText = 'SQLITE';
|
||
textClass = 'long';
|
||
break;
|
||
default:
|
||
// Для других расширений используем расширение или первые 4 символа
|
||
iconColor = '#7f8c8d';
|
||
iconText = extension.length > 4 ?
|
||
extension.substring(0, 4).toUpperCase() :
|
||
extension.toUpperCase();
|
||
|
||
// Определяем класс по длине текста
|
||
if (iconText.length <= 2) {
|
||
textClass = 'short';
|
||
} else if (iconText.length <= 4) {
|
||
textClass = 'medium';
|
||
} else {
|
||
textClass = 'long';
|
||
}
|
||
}
|
||
} else {
|
||
// Если нет расширения
|
||
iconColor = '#7f8c8d';
|
||
iconText = 'ФАЙЛ';
|
||
textClass = 'short';
|
||
}
|
||
|
||
// Исправляем кодировку для отображения
|
||
const safeFileName = fileName;
|
||
const displayFileName = truncateFileName(safeFileName);
|
||
|
||
return `
|
||
<a href="/api/files/${file.id}/download"
|
||
download="${encodeURIComponent(safeFileName)}"
|
||
class="file-icon-container"
|
||
title="${safeFileName} (${fileSize} MB) - Загрузил: ${uploadedBy}">
|
||
<div class="file-icon" style="background: ${iconColor}">
|
||
<span class="file-extension ${textClass}">${iconText}</span>
|
||
</div>
|
||
<div class="file-name">${displayFileName}</div>
|
||
</a>
|
||
`;
|
||
}
|
||
|
||
function truncateFileName(fileName, maxLength = 20) {
|
||
if (fileName.length <= maxLength) return fileName;
|
||
const extension = fileName.split('.').pop();
|
||
const name = fileName.substring(0, fileName.lastIndexOf('.'));
|
||
const truncatedName = name.substring(0, maxLength - extension.length - 3) + '...';
|
||
return truncatedName + '.' + extension;
|
||
}
|
||
|
||
// Вспомогательная функция для форматирования размера файла
|
||
function formatFileSize(bytes) {
|
||
if (bytes === 0) return '0 Bytes';
|
||
const k = 1024;
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
} |