добавил тестовую страницу загруженности аудиторий
This commit is contained in:
154
frontend/admin/css/auditorium-workload.css
Normal file
154
frontend/admin/css/auditorium-workload.css
Normal file
@@ -0,0 +1,154 @@
|
||||
/* ===== Auditorium Workload Specific Styles ===== */
|
||||
|
||||
.workload-grid-container {
|
||||
width: 100%;
|
||||
max-height: 600px;
|
||||
overflow: auto;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--bg-card-border);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.workload-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 800px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.workload-table th, .workload-table td {
|
||||
border: 1px solid var(--bg-card-border);
|
||||
padding: 0.5rem;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
min-width: 150px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.workload-table th {
|
||||
background: var(--bg-input);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
padding: 1rem 0.5rem;
|
||||
box-shadow: 0 1px 0 var(--bg-card-border);
|
||||
}
|
||||
|
||||
.workload-table .time-cell {
|
||||
background: var(--bg-input);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
box-shadow: 1px 0 0 var(--bg-card-border);
|
||||
}
|
||||
|
||||
.workload-table .top-left-cell {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
background: var(--bg-input);
|
||||
min-width: 120px;
|
||||
width: 120px;
|
||||
box-shadow: 1px 1px 0 var(--bg-card-border);
|
||||
}
|
||||
|
||||
/* Diagonal line using SVG or linear-gradient */
|
||||
.workload-table .top-left-cell::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(
|
||||
to bottom right,
|
||||
transparent calc(50% - 1px),
|
||||
var(--bg-card-border) 50%,
|
||||
transparent calc(50% + 1px)
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.top-left-cell span.top-label {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.top-left-cell span.bottom-label {
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
left: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Lesson Cards inside grid cells */
|
||||
.lesson-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--bg-card-border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.25rem;
|
||||
transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
[data-theme="light"] .lesson-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.lesson-card:hover {
|
||||
background: var(--bg-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.lesson-subject {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.2rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.lesson-group {
|
||||
font-weight: 700; /* Bolder specific for groups request mockup */
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.1rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.lesson-teacher {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Custom scrollbar adjustments for grid container */
|
||||
.workload-grid-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
.workload-grid-container::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.workload-grid-container::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 8px;
|
||||
}
|
||||
[data-theme="light"] .workload-grid-container::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
<link rel="stylesheet" href="css/modals.css">
|
||||
<link rel="stylesheet" href="css/department.css">
|
||||
<link rel="stylesheet" href="css/departments-data.css">
|
||||
<link rel="stylesheet" href="css/auditorium-workload.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -121,6 +122,14 @@
|
||||
</svg>
|
||||
<span>Расписание занятий</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-tab="auditorium-workload">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<line x1="3" y1="9" x2="21" y2="9"></line>
|
||||
<line x1="9" y1="21" x2="9" y2="9"></line>
|
||||
</svg>
|
||||
<span>Загруженность аудиторий</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-tab="database">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
|
||||
|
||||
@@ -28,6 +28,7 @@ import {initSchedule} from "./views/schedule.js";
|
||||
import {initDatabase} from "./views/database.js";
|
||||
import {initDepartment} from "./views/department.js";
|
||||
import {initDepartmentsData} from "./views/departments-data.js";
|
||||
import {initAuditoriumWorkload} from "./views/auditorium-workload.js";
|
||||
|
||||
// Configuration
|
||||
const ROUTES = {
|
||||
@@ -38,6 +39,7 @@ const ROUTES = {
|
||||
classrooms: { title: 'Аудитории', file: 'views/classrooms.html', init: initClassrooms },
|
||||
subjects: { title: 'Дисциплины и преподаватели', file: 'views/subjects.html', init: initSubjects },
|
||||
schedule: { title: 'Расписание занятий', file: 'views/schedule.html', init: initSchedule },
|
||||
'auditorium-workload': { title: 'Загруженность аудиторий', file: 'views/auditorium-workload.html', init: initAuditoriumWorkload },
|
||||
database: { title: 'База данных', file: 'views/database.html', init: initDatabase },
|
||||
department: { title: 'Кафедры', file: 'views/department.html', init: initDepartment },
|
||||
'departments-data': { title: 'Создание кафедры/специальности', file: 'views/departments-data.html', init: initDepartmentsData },
|
||||
|
||||
153
frontend/admin/js/views/auditorium-workload.js
Normal file
153
frontend/admin/js/views/auditorium-workload.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import { initMultiSelect } from '../utils.js';
|
||||
|
||||
export function initAuditoriumWorkload() {
|
||||
// Initialize date input with current date
|
||||
const dateInput = document.getElementById('workload-date');
|
||||
if (dateInput) {
|
||||
const today = new Date();
|
||||
const yyyy = today.getFullYear();
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(today.getDate()).padStart(2, '0');
|
||||
dateInput.value = `${yyyy}-${mm}-${dd}`;
|
||||
}
|
||||
|
||||
// Initialize Multi-Selects
|
||||
initMultiSelect('building-box', 'building-menu', 'building-text', 'building-checkboxes');
|
||||
initMultiSelect('capacity-box', 'capacity-menu', 'capacity-text', 'capacity-checkboxes');
|
||||
initMultiSelect('equipment-box', 'equipment-menu', 'equipment-text', 'equipment-checkboxes');
|
||||
|
||||
// Populate Filters with Mock/Initial Data
|
||||
populateFilters();
|
||||
|
||||
// Render Mock Data for the Grid based on the UI requested layout
|
||||
renderMockGrid();
|
||||
}
|
||||
|
||||
function populateFilters() {
|
||||
// Buildings
|
||||
const buildingsContainer = document.getElementById('building-checkboxes');
|
||||
const buildings = [
|
||||
{ id: 1, name: "Корпус 1 (Главный)" },
|
||||
{ id: 2, name: "Корпус 2 (Физ-мат)" },
|
||||
{ id: 3, name: "Корпус 3 (Гуманитарный)" }
|
||||
];
|
||||
buildingsContainer.innerHTML = buildings.map(item => `
|
||||
<label class="checkbox-item">
|
||||
<input type="checkbox" value="${item.id}">
|
||||
<span class="checkmark"></span>
|
||||
<span class="checkbox-label">${item.name}</span>
|
||||
</label>
|
||||
`).join('');
|
||||
|
||||
// Capacities
|
||||
const capacityContainer = document.getElementById('capacity-checkboxes');
|
||||
const capacities = [
|
||||
{ id: 'small', name: "До 30 мест" },
|
||||
{ id: 'medium', name: "30 - 60 мест" },
|
||||
{ id: 'large', name: "60 - 100 мест" },
|
||||
{ id: 'xlarge', name: "Более 100 мест" }
|
||||
];
|
||||
capacityContainer.innerHTML = capacities.map(item => `
|
||||
<label class="checkbox-item">
|
||||
<input type="checkbox" value="${item.id}">
|
||||
<span class="checkmark"></span>
|
||||
<span class="checkbox-label">${item.name}</span>
|
||||
</label>
|
||||
`).join('');
|
||||
|
||||
// Equipment
|
||||
const equipmentContainer = document.getElementById('equipment-checkboxes');
|
||||
const equipmentList = [
|
||||
{ id: 1, name: "Проектор" },
|
||||
{ id: 2, name: "Компьютерные места" },
|
||||
{ id: 3, name: "Интерактивная доска" },
|
||||
{ id: 4, name: "Микрофон" }
|
||||
];
|
||||
equipmentContainer.innerHTML = equipmentList.map(item => `
|
||||
<label class="checkbox-item">
|
||||
<input type="checkbox" value="${item.id}">
|
||||
<span class="checkmark"></span>
|
||||
<span class="checkbox-label">${item.name}</span>
|
||||
</label>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function renderMockGrid() {
|
||||
// In future this will be loaded from API
|
||||
const timeslots = [
|
||||
"8:00-9:30",
|
||||
"9:40-11:10",
|
||||
"11:40-13:10",
|
||||
"13:20-14:50",
|
||||
"15:00-16:30",
|
||||
"16:50-18:20",
|
||||
"18:30-19:50",
|
||||
"20:00-21:20"
|
||||
];
|
||||
|
||||
const auditoriums = [
|
||||
"201", "202", "204", "205", "206", "207", "208"
|
||||
];
|
||||
|
||||
// Mock schedule data mapped by room and time
|
||||
// Key: "roomId_timeSlotId", Value: Lesson object
|
||||
const mockSchedule = {
|
||||
"201_8:00-9:30": { subject: "Физика", group: "ИБ-41м", teacher: "Атлетов А.Р." },
|
||||
"201_9:40-11:10": { subject: "Физика", group: "ИВТ-21-1", teacher: "Атлетов А.Р." },
|
||||
"201_11:40-13:10": { subject: "Физика", group: "ИБ-41м", teacher: "Физик В.Г." },
|
||||
"201_13:20-14:50": { subject: "Физика", group: "ИБ-41м", teacher: "Физик В.Г." },
|
||||
|
||||
"202_9:40-11:10": { subject: "Химия", group: "ИВТ-21-1", teacher: "Химоза Я.В." },
|
||||
"202_13:20-14:50": { subject: "Математика", group: "ИВТ-21-1", teacher: "Рутина Л.П." },
|
||||
"202_15:00-16:30": { subject: "Химия", group: "ИВТ-21-1", teacher: "Химоза Я.В." },
|
||||
"202_16:50-18:20": { subject: "Физика", group: "ИВТ-21-1", teacher: "Атлетов А.Р." },
|
||||
|
||||
"205_9:40-11:10": { subject: "Организация аудита ИБ", group: "ИБ-41м", teacher: "Таныгин М.О." },
|
||||
};
|
||||
|
||||
// Render Headers
|
||||
const headerRow = document.getElementById('workload-header-row');
|
||||
// Start after the first fixed cell (which is already in HTML)
|
||||
|
||||
auditoriums.forEach(room => {
|
||||
const th = document.createElement('th');
|
||||
th.textContent = room;
|
||||
headerRow.appendChild(th);
|
||||
});
|
||||
|
||||
// Render Body Rows
|
||||
const tbody = document.getElementById('workload-tbody');
|
||||
|
||||
timeslots.forEach((time) => {
|
||||
const tr = document.createElement('tr');
|
||||
|
||||
// Add Time Cell
|
||||
const tdTime = document.createElement('td');
|
||||
tdTime.className = 'time-cell';
|
||||
tdTime.textContent = time;
|
||||
tr.appendChild(tdTime);
|
||||
|
||||
// Add Room Cells for this Time
|
||||
auditoriums.forEach(room => {
|
||||
const td = document.createElement('td');
|
||||
|
||||
const scheduleKey = `${room}_${time}`;
|
||||
const lesson = mockSchedule[scheduleKey];
|
||||
|
||||
if (lesson) {
|
||||
// Render lesson card
|
||||
td.innerHTML = `
|
||||
<div class="lesson-card">
|
||||
<div class="lesson-subject">${lesson.subject}</div>
|
||||
<div class="lesson-group">${lesson.group}</div>
|
||||
<div class="lesson-teacher">${lesson.teacher}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
tr.appendChild(td);
|
||||
});
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
75
frontend/admin/views/auditorium-workload.html
Normal file
75
frontend/admin/views/auditorium-workload.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<div class="card">
|
||||
<div class="card-header-row" style="margin-bottom: 1.5rem;">
|
||||
<h2>Загруженность аудиторий</h2>
|
||||
</div>
|
||||
|
||||
<div class="filter-row" style="margin-bottom: 2rem; align-items: flex-end; gap: 1.5rem;">
|
||||
<div class="form-group">
|
||||
<label>Корпус</label>
|
||||
<div class="custom-multi-select">
|
||||
<div class="select-box" id="building-box">
|
||||
<span class="select-text" id="building-text">Выберите корпуса...</span>
|
||||
<svg class="dropdown-icon" width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 1.5L6 6.5L11 1.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="building-menu">
|
||||
<div id="building-checkboxes" class="checkbox-group-vertical"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Вместимость</label>
|
||||
<div class="custom-multi-select">
|
||||
<div class="select-box" id="capacity-box">
|
||||
<span class="select-text" id="capacity-text">Выберите вместимость...</span>
|
||||
<svg class="dropdown-icon" width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 1.5L6 6.5L11 1.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="capacity-menu">
|
||||
<div id="capacity-checkboxes" class="checkbox-group-vertical"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Оборудование</label>
|
||||
<div class="custom-multi-select">
|
||||
<div class="select-box" id="equipment-box">
|
||||
<span class="select-text" id="equipment-text">Выберите оборудование...</span>
|
||||
<svg class="dropdown-icon" width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 1.5L6 6.5L11 1.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="equipment-menu">
|
||||
<div id="equipment-checkboxes" class="checkbox-group-vertical"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="max-width: 200px;">
|
||||
<label>Дата</label>
|
||||
<input type="date" id="workload-date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table Container -->
|
||||
<div class="workload-grid-container">
|
||||
<table class="workload-table" id="workload-table">
|
||||
<thead>
|
||||
<tr id="workload-header-row">
|
||||
<th class="top-left-cell">
|
||||
<span class="top-label">Аудитория</span>
|
||||
<span class="bottom-label">Время</span>
|
||||
</th>
|
||||
<!-- Rendered by JS -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="workload-tbody">
|
||||
<!-- Rendered by JS -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user