r
This commit is contained in:
368
public/script.js
368
public/script.js
@@ -214,11 +214,24 @@ async function loadTasks() {
|
||||
|
||||
const response = await fetch(url);
|
||||
tasks = await response.json();
|
||||
|
||||
// Загружаем файлы для всех задач
|
||||
await Promise.all(tasks.map(async (task) => {
|
||||
try {
|
||||
const filesResponse = await fetch(`/api/tasks/${task.id}/files`);
|
||||
if (filesResponse.ok) {
|
||||
task.files = await filesResponse.json();
|
||||
} else {
|
||||
task.files = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Ошибка загрузки файлов для задачи ${task.id}:`, error);
|
||||
task.files = [];
|
||||
}
|
||||
}));
|
||||
|
||||
renderTasks();
|
||||
|
||||
tasks.forEach(task => {
|
||||
loadTaskFiles(task.id);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки задач:', error);
|
||||
}
|
||||
@@ -244,6 +257,21 @@ async function loadTasksWithoutDate() {
|
||||
return hasTaskDueDate && hasAssignmentDueDates;
|
||||
});
|
||||
|
||||
// Загружаем файлы для всех задач
|
||||
await Promise.all(tasks.map(async (task) => {
|
||||
try {
|
||||
const filesResponse = await fetch(`/api/tasks/${task.id}/files`);
|
||||
if (filesResponse.ok) {
|
||||
task.files = await filesResponse.json();
|
||||
} else {
|
||||
task.files = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Ошибка загрузки файлов для задачи ${task.id}:`, error);
|
||||
task.files = [];
|
||||
}
|
||||
}));
|
||||
|
||||
renderTasks();
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки задач без срока:', error);
|
||||
@@ -389,7 +417,11 @@ function renderTasks() {
|
||||
${showingTasksWithoutDate ? '<span class="no-date-badge">Без срока</span>' : ''}
|
||||
</div>
|
||||
<div class="file-list" id="files-${task.id}">
|
||||
<strong>Файлы:</strong> <span class="files-placeholder">скрыто</span>
|
||||
<strong>Файлы:</strong>
|
||||
${task.files && task.files.length > 0 ?
|
||||
`<div class="file-icons-container">${task.files.map(file => renderFileIcon(file)).join('')}</div>` :
|
||||
'<span class="no-files">нет файлов</span>'
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -417,6 +449,7 @@ function toggleTask(taskId) {
|
||||
expandedTasks.delete(taskId);
|
||||
} else {
|
||||
expandedTasks.add(taskId);
|
||||
loadTaskFiles(taskId); // Эта строка должна быть
|
||||
}
|
||||
renderTasks();
|
||||
}
|
||||
@@ -1122,19 +1155,328 @@ async function loadTaskFiles(taskId) {
|
||||
} else {
|
||||
container.innerHTML = `
|
||||
<strong>Файлы:</strong>
|
||||
${files.map(file => `
|
||||
<div class="file-item">
|
||||
<a href="/api/files/${file.id}/download" download="${file.original_name}">
|
||||
${file.original_name}
|
||||
</a>
|
||||
(${(file.file_size / 1024 / 1024).toFixed(2)} MB)
|
||||
<small> - загрузил: ${file.user_name}</small>
|
||||
</div>
|
||||
`).join('')}
|
||||
<div class="file-icons-container">
|
||||
${files.map(file => renderFileIcon(file)).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки файлов:', error);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user