Добавил:
МТУСИ Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ЛР3 / Таблица

.html
Скачиваний:
0
Добавлен:
21.04.2026
Размер:
18.68 Кб
Скачать

Лабораторная работа №3 - Диаграмма Ганта /* Глобальный сброс для строгой вёрстки */ * { box-sizing: border-box; } body { font-family: 'Times New Roman', Times, serif; margin: 20px; background-color: #fff; color: #000; } h2 { text-align: center; font-weight: bold; } .gantt-container { display: flex; margin-top: 20px; border: 2px solid #000; height: auto; } /* Левая таблица */ .task-list { flex-shrink: 0; width: 350px; border-right: 2px solid #000; background-color: #f9f9f9; z-index: 20; position: relative; } .task-header, .task-row { display: flex; border-bottom: 1px solid #000; height: 40px; line-height: 40px; font-size: 14px; } .task-header { font-weight: bold; background-color: #e0e0e0; text-align: center; } .col-id { width: 40px; text-align: center; border-right: 1px solid #000; } .col-name { padding-left: 5px; flex-grow: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .col-duration { width: 60px; text-align: center; border-left: 1px solid #000; } /* Правая часть с графиком */ .timeline-wrapper { flex-grow: 1; overflow-x: auto; overflow-y: hidden; position: relative; background-color: #fff; } .timeline-inner { position: relative; /* Ширина рассчитывается JS */ } /* Заголовки (Месяцы + Дни) */ .timeline-header { position: sticky; top: 0; background-color: #fff; z-index: 10; border-bottom: 2px solid #000; height: 60px; display: flex; flex-direction: column; } .months-row { display: flex; height: 30px; border-bottom: 1px solid #000; } .days-row { display: flex; height: 30px; } .month-label { border-right: 1px solid #000; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px; background-color: #e0e0e0; white-space: nowrap; overflow: hidden; } /* Жёсткая ячейка дня */ .day-cell { width: 20px; min-width: 20px; /* Запрет сжатия */ max-width: 20px; flex-shrink: 0; text-align: center; font-size: 10px; line-height: 30px; border-right: 1px solid #ccc; color: #333; background-color: #fff; } .day-cell.weekend-header { background-color: #f0f0f0; color: #999; } /* Область отрисовки баров */ .chart-area { position: relative; } /* Сетка и выходные */ .grid-column { position: absolute; top: 0; bottom: 0; width: 20px; border-right: 1px solid #ddd; z-index: 1; } .grid-column.weekend-bg { background-color: #e8e8e8; /* Темный фон выходных */ } /* Бары задач */ .bar { position: absolute; height: 20px; top: 10px; background-color: #4472C4; /* Синий цвет */ border-radius: 2px; z-index: 5; box-sizing: border-box; cursor: pointer; white-space: nowrap; } .bar:hover { opacity: 0.9; box-shadow: 0 0 8px rgba(0,0,0,0.6); } /* Красная метка для выходных внутри задачи */ .weekend-marker { position: absolute; top: 0; bottom: 0; width: 20px; background-color: #ff4d4d; /* Красный цвет */ opacity: 0.7; z-index: 6; } /* Tooltip (Подсказка) */ .tooltip { position: absolute; background: #333; color: #fff; padding: 5px 10px; border-radius: 4px; font-size: 12px; pointer-events: none; opacity: 0; transition: opacity 0.2s; z-index: 100; white-space: nowrap; transform: translateY(-50%); margin-left: 10px; } .bar:hover .tooltip { opacity: 1; } .row-grid-line { position: absolute; left: 0; right: 0; height: 40px; border-bottom: 1px solid #eee; z-index: 0; } .total-duration { margin-top: 10px; text-align: center; font-weight: bold; font-size: 16px; } Лабораторная работа № 3

«Линейные модели управления проектами» № Наименование работы Дни // --- 1. ДАННЫЕ --- const tasks = [ { id: 1, name: "Разработка ТЗ", duration: 5, deps: [] }, { id: 2, name: "Выбор скелетной схемы", duration: 10, deps: [1] }, { id: 3, name: "Определение необходимого числа вариантов разработки", duration: 4, deps: [2] }, { id: 4, name: "Проверка на макетах", duration: 10, deps: [3] }, { id: 5, name: "Расчет скелетной схемы", duration: 5, deps: [3] }, { id: 6, name: "Обработка результатов макетирования", duration: 5, deps: [4, 5] }, { id: 7, name: "Составление принципиальной схемы", duration: 5, deps: [6] }, { id: 8, name: "Расчет принципиальной схемы", duration: 15, deps: [7] }, { id: 9, name: "Блочное проектирование", duration: 10, deps: [7] }, { id: 10, name: "Определение допусков на электрические параметры", duration: 3, deps: [8, 9] }, // ИЗМЕНЕНИЕ 1: Чертежи (16) идут сразу после допусков (10), параллельно с макетированием { id: 16, name: "Изготовление рабочих чертежей", duration: 20, deps: [10] }, // ИЗМЕНЕНИЕ 2: Техпроцесс (11) и Оснастка (12) идут ПОСЛЕ чертежей (16) { id: 11, name: "Разработка маршрутной технологии", duration: 12, deps: [16] }, { id: 12, name: "Проектирование оснастки", duration: 15, deps: [11] }, // Макетирование идет своим путем, но Испытание (14) больше не зависит от чертежей/оснастки { id: 13, name: "Изготовление макета", duration: 5, deps: [10] }, { id: 14, name: "Испытание макета", duration: 5, deps: [13] }, { id: 15, name: "Опробование оснастки", duration: 5, deps: [12] }, { id: 17, name: "Передача чертежей в цех опытного производства", duration: 1, deps: [16] }, { id: 18, name: "Изготовление опытного образца", duration: 10, deps: [14, 15, 17] }, { id: 19, name: "Уточнение техпроцесса", duration: 5, deps: [18] }, { id: 20, name: "Корректировка рабочих чертежей", duration: 7, deps: [18] }, { id: 21, name: "Производство первой серии телевизоров", duration: 20, deps: [19, 20] }, { id: 22, name: "Комплектация рабочих чертежей", duration: 4, deps: [20], startWith: 21 }, { id: 23, name: "Составление технических указаний", duration: 5, deps: [20], startWith: 21 }, { id: 24, name: "Передача документации в производство", duration: 3, deps: [21, 22, 23] } ]; const projectStartDate = new Date(2026, 2, 3); // 03.03.2026 const dayWidth = 20; // --- 2. ЛОГИКА РАСЧЕТА ДАТ --- function addWorkingDays(startDate, days) { let date = new Date(startDate); let count = 0; while (count < days) { date.setDate(date.getDate() + 1); if (date.getDay() !== 0 && date.getDay() !== 6) count++; } return date; } tasks.forEach(task => { if (task.startWith) { const parentTask = tasks.find(t => t.id === task.startWith); if (parentTask) { task.start = new Date(parentTask.start); if (task.deps && task.deps.length > 0) { let maxDepEnd = new Date(0); task.deps.forEach(depId => { const dep = tasks.find(t => t.id === depId); if (dep && dep.end > maxDepEnd) maxDepEnd = dep.end; }); if (maxDepEnd > task.start) task.start = maxDepEnd; } } } else { let maxDepEnd = projectStartDate; if (task.deps && task.deps.length > 0) { task.deps.forEach(depId => { const dep = tasks.find(t => t.id === depId); if (dep && dep.end > maxDepEnd) maxDepEnd = dep.end; }); task.start = new Date(maxDepEnd); task.start.setDate(task.start.getDate() + 1); } else { task.start = new Date(projectStartDate); } } if (task.start.getDay() === 0) task.start.setDate(task.start.getDate() + 1); if (task.start.getDay() === 6) task.start.setDate(task.start.getDate() + 2); task.end = addWorkingDays(task.start, task.duration - 1); task.startDateStr = task.start.toLocaleDateString('ru-RU'); task.endDateStr = task.end.toLocaleDateString('ru-RU'); }); // --- 3. ВИЗУАЛИЗАЦИЯ --- const rowHeight = 40; const minDate = projectStartDate.getTime(); const maxDate = Math.max(...tasks.map(t => t.end.getTime())); let currentDay = new Date(projectStartDate); let endGridDate = new Date(maxDate); endGridDate.setDate(endGridDate.getDate() + 20); // Запас const days = []; while (currentDay { // Числа const dayCell = document.createElement('div'); dayCell.className = 'day-cell'; dayCell.textContent = day.getDate(); if (day.getDay() === 0 || day.getDay() === 6) { dayCell.classList.add('weekend-header'); } daysRow.appendChild(dayCell); // Месяцы if (day.getMonth() !== currentMonth) { if (currentMonth !== -1) { const monthDiv = document.createElement('div'); monthDiv.className = 'month-label'; const width = (index - monthStartIndex) * dayWidth; monthDiv.style.width = width + 'px'; monthDiv.textContent = monthNames[currentMonth] + " " + days[index-1].getFullYear(); monthsRow.appendChild(monthDiv); } currentMonth = day.getMonth(); monthStartIndex = index; } }); const lastMonthDiv = document.createElement('div'); lastMonthDiv.className = 'month-label'; const lastWidth = (days.length - monthStartIndex) * dayWidth; lastMonthDiv.style.width = lastWidth + 'px'; lastMonthDiv.textContent = monthNames[currentMonth] + " " + days[days.length-1].getFullYear(); monthsRow.appendChild(lastMonthDiv); // --- Сетка --- days.forEach((day, index) => { const col = document.createElement('div'); col.className = 'grid-column'; col.style.left = (index * dayWidth) + 'px'; if (day.getDay() === 0 || day.getDay() === 6) { col.classList.add('weekend-bg'); } chartArea.appendChild(col); }); // --- Отрисовка Задач --- tasks.forEach((task, index) => { // Таблица const row = document.createElement('div'); row.className = 'task-row'; row.innerHTML = `${task.id}${task.name}${task.duration}`; taskListContainer.appendChild(row); // Линии строк const rowLine = document.createElement('div'); rowLine.className = 'row-grid-line'; rowLine.style.top = (index * rowHeight) + 'px'; chartArea.appendChild(rowLine); // Бар задачи const bar = document.createElement('div'); bar.className = 'bar'; const startIndex = days.findIndex(d => d.getTime() === task.start.getTime()); const endIndex = days.findIndex(d => d.getTime() === task.end.getTime()); if (startIndex !== -1 && endIndex !== -1) { const left = startIndex * dayWidth; const width = ((endIndex - startIndex) + 1) * dayWidth; bar.style.left = left + 'px'; bar.style.width = width + 'px'; bar.style.top = (index * rowHeight) + 'px'; // Логика красных меток для выходных дней for (let i = startIndex; i

Соседние файлы в папке ЛР3