документы

This commit is contained in:
2026-02-24 23:08:35 +05:00
parent 27b7197569
commit 72541292f5
4 changed files with 645 additions and 285 deletions

View File

@@ -91,9 +91,11 @@ function showMainInterface() {
function reloadAllScripts() {
//console.log('🔄 Перезагрузка всех скриптов после авторизации...');
// Список скриптов для перезагрузки (в правильном порядке)
// Список скриптов для перезагрузки (в правильном порядке)
// 'loading-start.js',
// 'document-fields.js',
const scriptsToReload = [
'loading-start.js',
'users.js',
'ui.js',
'signature.js',
@@ -106,7 +108,6 @@ function reloadAllScripts() {
'tasks_files.js',
'navbar.js',
'chat-ui.js',
'document-fields.js',
'main.js'
];

View File

@@ -4420,4 +4420,238 @@ button.btn-primary {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}
}
.document-fields-container {
margin: 12px 0;
padding: 12px 15px;
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2fa 100%);
border-left: 4px solid #4a90e2;
border-radius: 0 8px 8px 0;
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
font-size: 0.95em;
width: 100%;
box-sizing: border-box;
}
.document-fields-container.empty {
background: linear-gradient(135deg, #f9f9f9 0%, #f0f0f0 100%);
border-left-color: #9e9e9e;
opacity: 0.8;
}
.document-fields-header {
display: flex;
align-items: center;
margin-bottom: 10px;
padding-bottom: 6px;
border-bottom: 1px dashed rgba(74, 144, 226, 0.3);
width: 100%;
}
.document-fields-container.empty .document-fields-header {
border-bottom-color: rgba(158, 158, 158, 0.3);
}
.document-icon {
font-size: 1.2em;
margin-right: 8px;
filter: drop-shadow(0 2px 2px rgba(0,0,0,0.1));
flex-shrink: 0;
}
.document-title {
font-weight: 600;
color: #2c3e50;
text-transform: uppercase;
font-size: 0.85em;
letter-spacing: 0.5px;
white-space: nowrap;
}
.document-fields-container.empty .document-title {
color: #7f8c8d;
}
/* Горизонтальное расположение полей */
.document-fields-content {
display: flex;
flex-wrap: wrap;
gap: 12px 20px;
align-items: center;
width: 100%;
}
/* Стили для каждого поля - текст по центру вертикально */
.document-field {
display: inline-flex;
align-items: center; /* Выравнивание по центру вертикально */
background: white;
padding: 4px 14px; /* Уменьшил верхний/нижний padding для лучшего выравнивания */
border-radius: 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
border: 1px solid rgba(0,0,0,0.03);
transition: all 0.2s ease;
max-width: 100%;
flex: 0 1 auto;
line-height: 1.4; /* Добавил для единообразия высоты строки */
}
.document-field:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.08);
transform: translateY(-1px);
}
.document-field.long-value {
max-width: 300px;
}
.document-fields-container.empty .document-field {
background: rgba(255,255,255,0.7);
}
.field-label {
font-weight: 500;
color: #7f8c8d;
margin-right: 6px;
font-size: 0.9em;
white-space: nowrap;
flex-shrink: 0;
line-height: 1.4; /* Единообразие высоты строки */
}
.field-value {
font-weight: 600;
color: #2c3e50;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
line-height: 1.4; /* Единообразие высоты строки */
padding: 2px 0; /* Небольшой отступ для лучшего выравнивания */
}
.document-field.long-value .field-value {
max-width: 250px;
}
/* Специфичные стили для разных типов полей */
.document-number {
color: #e67e22;
background: rgba(230, 126, 34, 0.1);
padding: 2px 10px;
border-radius: 20px;
display: inline-block;
}
.document-date {
color: #27ae60;
background: rgba(39, 174, 96, 0.1);
padding: 2px 10px;
border-radius: 20px;
display: inline-block;
}
.document-author {
color: #3498db;
background: rgba(52, 152, 219, 0.1);
padding: 2px 10px;
border-radius: 20px;
display: inline-block;
}
/* Стили для пустого состояния */
.document-fields-empty {
display: flex;
align-items: center;
justify-content: center;
padding: 8px 0;
width: 100%;
}
.empty-message {
color: #95a5a6;
font-style: italic;
font-size: 0.9em;
display: flex;
align-items: center;
gap: 5px;
}
.empty-message::before {
content: "⏳";
font-size: 1.1em;
opacity: 0.7;
}
/* Анимация появления */
.document-fields-container {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Адаптивность для мобильных и узких экранов */
@media (max-width: 768px) {
.document-fields-content {
flex-direction: column;
align-items: stretch;
gap: 8px;
}
.document-field {
width: 100%;
max-width: 100%;
box-sizing: border-box;
justify-content: flex-start; /* Выравнивание по левому краю на мобильных */
}
.document-field.long-value {
max-width: 100%;
}
.field-value {
max-width: calc(100% - 60px);
}
.document-field.long-value .field-value {
max-width: calc(100% - 60px);
}
}
/* Для очень узких экранов */
@media (max-width: 480px) {
.document-fields-header {
flex-wrap: wrap;
}
.document-title {
white-space: normal;
word-break: break-word;
}
.document-field {
flex-wrap: wrap;
padding: 8px 12px;
justify-content: flex-start;
}
.field-label {
margin-bottom: 4px;
}
.field-value {
white-space: normal;
word-break: break-word;
max-width: 100%;
}
}

View File

@@ -22,11 +22,11 @@ async function loadTasks() {
}
if (creatorFilter) {
params.push(`creator=${encodeURIComponent(creatorFilter)}`); // было creator_id, теперь creator
params.push(`creator=${encodeURIComponent(creatorFilter)}`);
}
if (assigneeFilter) {
params.push(`assignee=${encodeURIComponent(assigneeFilter)}`); // было assignee_id, теперь assignee
params.push(`assignee=${encodeURIComponent(assigneeFilter)}`);
}
if (deadlineFilter) {
@@ -38,7 +38,7 @@ async function loadTasks() {
}
if (typeFilter) {
params.push(`task_type=${encodeURIComponent(typeFilter)}`); // было type, теперь task_type
params.push(`task_type=${encodeURIComponent(typeFilter)}`);
}
url += params.join('&');
@@ -53,6 +53,12 @@ async function loadTasks() {
tasks = await response.json();
console.log(`Загружено ${tasks.length} задач`);
// Загружаем поля документа для задач типа "document"
await loadDocumentFieldsForTasks();
// Загружаем файлы для развернутых задач
await loadFilesForExpandedTasks();
// Обновляем отображение
const activeSection = document.querySelector('.section.active');
if (activeSection) {
@@ -77,12 +83,64 @@ async function loadTasks() {
}
}
// Новая функция для загрузки полей документов
async function loadDocumentFieldsForTasks() {
const documentTasks = tasks.filter(task => task.task_type === 'document');
if (documentTasks.length === 0) {
return;
}
console.log(`Загрузка полей документов для ${documentTasks.length} задач`);
// Загружаем поля для каждой задачи
for (const task of documentTasks) {
try {
const docResponse = await fetch(`/api/tasks/${task.id}/document-fields`);
if (docResponse.ok) {
const docData = await docResponse.json();
task.document_fields = docData.data || {};
console.log(`✅ Загружены поля для задачи ${task.id}:`, task.document_fields);
} else {
task.document_fields = {};
}
} catch (error) {
console.error(`Ошибка загрузки полей документа для задачи ${task.id}:`, error);
task.document_fields = {};
}
}
}
// Новая функция для загрузки файлов развернутых задач
async function loadFilesForExpandedTasks() {
const expandedTasksArray = tasks.filter(task => expandedTasks.has(task.id));
if (expandedTasksArray.length === 0) {
return;
}
await Promise.all(expandedTasksArray.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 = [];
}
}));
}
function showTasksWithoutDate() {
showingTasksWithoutDate = true;
const btn = document.getElementById('tasks-no-date-btn');
if (btn) btn.classList.add('active');
loadTasksWithoutDate();
}
function resetAllFilters() {
document.getElementById('status-filter').value = 'active,in_progress,assigned,overdue,rework';
document.getElementById('creator-filter').value = '';
@@ -92,6 +150,7 @@ function resetAllFilters() {
document.getElementById('search-tasks').value = '';
loadTasks();
}
async function loadTasksWithoutDate() {
try {
const response = await fetch('/api/tasks');
@@ -120,14 +179,15 @@ async function loadTasksWithoutDate() {
}
}));
// Загружаем поля документов
await loadDocumentFieldsForTasks();
renderTasks();
} catch (error) {
console.error('Ошибка загрузки задач без срока:', error);
}
}
// удален async function createTask(event)
async function openEditModal(taskId) {
try {
const response = await fetch(`/api/tasks/${taskId}`);
@@ -188,7 +248,6 @@ async function updateTask(event) {
return;
}
// Используем editSelectedUsers
const assignedUserIds = editSelectedUsers;
const formData = new FormData();
@@ -226,12 +285,10 @@ async function updateTask(event) {
function openCopyModal(taskId) {
document.getElementById('copy-task-id').value = taskId;
// Устанавливаем дату по умолчанию (через 7 дней)
const defaultDate = new Date();
defaultDate.setDate(defaultDate.getDate() + 7);
document.getElementById('copy-due-date').value = defaultDate.toISOString().substring(0, 16);
// Сбрасываем выбранных пользователей
copySelectedUsers = [];
renderCopyUsersChecklist(users);
@@ -256,7 +313,6 @@ async function copyTask(event) {
return;
}
// Используем copySelectedUsers
const assignedUserIds = copySelectedUsers;
if (assignedUserIds.length === 0) {
@@ -501,53 +557,41 @@ async function updateStatus(taskId, userId, status) {
function canUserEditTask(task) {
if (!currentUser) return false;
// Администратор может всё
if (currentUser.role === 'admin') return true;
// Пользователи с ролью 'tasks' могут редактировать любые задачи
if (currentUser.role === 'tasks') return true;
// Создатель может редактировать свою задачу
if (parseInt(task.created_by) === currentUser.id) {
// Но если задача уже назначена другим пользователям,
// создатель может только просматривать
if (task.assignments && task.assignments.length > 0) {
// Проверяем, назначена ли задача другим пользователям (не только себе)
const assignedToOthers = task.assignments.some(assignment =>
parseInt(assignment.user_id) !== currentUser.id
);
if (assignedToOthers) {
// Создатель может только просматривать и закрывать задачу
return true;
}
}
return true;
}
// Исполнитель может менять только свой статус
if (task.assignments) {
const isExecutor = task.assignments.some(assignment =>
parseInt(assignment.user_id) === currentUser.id
);
if (isExecutor) {
// Исполнитель может менять только статус
return false;
}
}
return false;
}
// функция для проверки прав на добавление файлов
function canUserAddFilesToTask(task) {
if (!currentUser) return false;
// Администратор может всё
if (currentUser.role === 'admin') return true;
// Создатель задачи может добавлять файлы
if (parseInt(task.created_by) === currentUser.id) return true;
// Исполнитель задачи может добавлять файлы
if (task.assignments) {
const isExecutor = task.assignments.some(assignment =>
parseInt(assignment.user_id) === currentUser.id
@@ -556,4 +600,22 @@ function canUserAddFilesToTask(task) {
}
return false;
}
}
// Добавляем отладочную функцию
function debugDocumentFields() {
console.log('=== ОТЛАДКА ПОЛЕЙ ДОКУМЕНТОВ ===');
const documentTasks = tasks.filter(task => task.task_type === 'document');
console.log(`Найдено ${documentTasks.length} задач типа "document"`);
documentTasks.forEach(task => {
console.log(`Задача ${task.id}:`, {
title: task.title,
document_fields: task.document_fields || 'НЕТ ПОЛЕЙ'
});
});
console.log('================================');
}
// Вызываем отладку после загрузки (можно вызвать вручную из консоли)
window.debugDocumentFields = debugDocumentFields;

File diff suppressed because it is too large Load Diff