Доработано создание дисциплин и отображение новых параметров дисциплин в таблице+страничка с кафедрами(голая)

This commit is contained in:
2026-03-19 23:42:09 +03:00
parent 8cf086d3e9
commit d78e675a71
7 changed files with 471 additions and 5 deletions

View File

@@ -0,0 +1,235 @@
.wrap{
max-width: 900px;
margin: 0 auto;
background: var(--bg-card);
border: 1px solid var(--bg-card-border);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 6px 20px rgba(0,0,0,.06);
}
.header{
padding: 14px 16px;
border-bottom: 1px solid var(--bg-card-border);
font-weight: 700;
color: var(--text-primary);
}
details.table-item{
border-top: 1px solid var(--bg-card-border);
}
details.table-item:first-of-type{ border-top:none; }
summary{
list-style: none;
cursor: pointer;
user-select: none;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 10px;
}
summary::-webkit-details-marker{ display:none; }
.chev{
width: 28px;
height: 28px;
border: 1px solid var(--bg-card-border);
border-radius: 10px;
display: grid;
place-items: center;
flex: 0 0 auto;
color: var(--text-secondary);
background: var(--bg-input);
transition: transform .18s ease, color .18s ease, border-color .18s ease, background .18s ease;
}
.chev-icon{
width: 16px;
height: 16px;
display: block;
}
summary:hover .chev{
background: var(--bg-hover);
border-color: color-mix(in srgb, var(--accent) 22%, var(--bg-card-border));
color: var(--text-primary);
}
details[open] .chev{
transform: rotate(180deg);
color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 35%, var(--bg-card-border));
background: color-mix(in srgb, var(--accent) 10%, var(--bg-input));
}
.meta{ color: var(--text-secondary); font-size: 12px; }
.content{ padding: 0 16px 16px 16px; }
.wrap table{
width: 100%;
border-collapse: collapse;
border: 1px solid var(--bg-card-border);
border-radius: 10px;
overflow: hidden;
background: var(--bg-card);
}
.wrap thead th{
text-align: left;
font-size: 13px;
color: var(--text-secondary);
background: var(--bg-input);
border-bottom: 1px solid var(--bg-card-border);
padding: 10px 12px;
}
.wrap tbody td{
padding: 10px 12px;
border-bottom: 1px solid var(--bg-card-border);
font-size: 14px;
color: var(--text-primary);
}
.wrap tbody tr:hover{ background: var(--bg-hover); }
.title-multiline{
display: flex;
flex-direction: column;
gap: 2px;
line-height: 1.2;
}
.title-multiline .title-main{
font-weight: 700;
color: var(--text-primary);
}
.title-multiline .title-sub{
font-weight: 500;
font-size: 12px;
color: var(--text-secondary);
}
.title-multiline b{
font-weight: 700;
color: var(--text-primary);
}
/* summary = 3 колонки: [chev] [title] [meta] */
details.table-item > summary{
display: grid;
grid-template-columns: 28px 1fr auto;
gap: 12px;
align-items: start; /* важно: всё прижимаем к верху */
padding: 12px 16px;
}
/* чтобы текст нормально переносился и не растягивал мету */
details.table-item > summary .title{
min-width: 0; /* важно для grid, иначе может распирать */
}
/* "2 записи" всегда справа и сверху, аккуратно */
details.table-item > summary .meta{
justify-self: end;
align-self: start;
white-space: nowrap;
padding-top: 4px; /* чуть опустить относительно первой строки */
font-size: 12px;
color: var(--text-secondary);
}
/* стрелка тоже сверху */
details.table-item > summary .chev{
align-self: start;
margin-top: 2px;
}
.records-search{
width: min(360px, 60vw);
padding: 0.45rem 0.7rem;
background: var(--bg-input);
border: 1px solid var(--bg-card-border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 0.9rem;
outline: none;
transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
}
.records-search::placeholder{ color: var(--text-placeholder); }
.records-search:focus{
background: var(--bg-input-focus);
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-glow);
}
/* Таблица внутри раскрывающегося блока */
details.table-item .content table{
width: 100%;
border-collapse: separate; /* нужно для красивых линий */
border-spacing: 0;
border: 1px solid var(--bg-card-border);
border-radius: 12px;
overflow: hidden;
background: var(--bg-card);
}
/* Шапка */
details.table-item .content thead th{
position: sticky; /* опционально: шапка прилипает при скролле */
top: 0;
z-index: 1;
background: var(--bg-input);
color: var(--text-secondary);
border-bottom: 1px solid var(--bg-card-border);
}
/* Ячейки: одинаковые отступы */
details.table-item .content th,
details.table-item .content td{
padding: 0.75rem 0.85rem;
vertical-align: top;
}
/* Вертикальные разделители между колонками */
details.table-item .content th:not(:last-child),
details.table-item .content td:not(:last-child){
border-right: 1px solid var(--bg-card-border);
}
/* Горизонтальные разделители между строками */
details.table-item .content tbody td{
border-bottom: 1px solid var(--bg-card-border);
color: var(--text-primary);
}
/* У последней строки нет нижней линии */
details.table-item .content tbody tr:last-child td{
border-bottom: none;
}
/* "Зебра" для читабельности */
details.table-item .content tbody tr:nth-child(even){
background: color-mix(in srgb, var(--bg-card) 70%, var(--bg-hover));
}
/* Ховер по строке */
details.table-item .content tbody tr:hover{
background: var(--bg-hover);
}
/* (Опционально) Чтобы длинный текст не ломал ширину */
details.table-item .content td{
word-break: break-word;
}
/* (Опционально) если таблица широкая — пусть скроллится горизонтально */
details.table-item .content{
overflow-x: auto;
}

View File

@@ -14,6 +14,7 @@
<link rel="stylesheet" href="css/layout.css">
<link rel="stylesheet" href="css/components.css">
<link rel="stylesheet" href="css/modals.css">
<link rel="stylesheet" href="css/department.css">
</head>
<body>
@@ -46,6 +47,17 @@
</svg>
Пользователи
</a>
<a href="#" class="nav-item" data-tab="department">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 21V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v16" />
<path d="M2 21h20" />
<path d="M8 7h0M12 7h0M16 7h0" />
<path d="M8 11h0M12 11h0M16 11h0" />
<path d="M10 21v-4h4v4" />
</svg>
Кафедра
</a>
<a href="#" class="nav-item" data-tab="groups">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">

View File

@@ -9,6 +9,7 @@ import { initClassrooms } from './views/classrooms.js';
import { initSubjects } from './views/subjects.js';
import {initSchedule} from "./views/schedule.js";
import {initDatabase} from "./views/database.js";
import {initDepartment} from "./views/department.js";
// Configuration
const ROUTES = {
@@ -20,6 +21,7 @@ const ROUTES = {
subjects: { title: 'Дисциплины и преподаватели', file: 'views/subjects.html', init: initSubjects },
schedule: { title: 'Расписание занятий', file: 'views/schedule.html', init: initSchedule },
database: { title: 'База данных', file: 'views/database.html', init: initDatabase },
department: { title: 'Кафедры', file: 'views/department.html', init: initDepartment },
};
let currentTab = null;

View File

@@ -0,0 +1,4 @@
import { api } from '../api.js';
import { escapeHtml } from '../utils.js';
export async function initDepartment() { }

View File

@@ -24,19 +24,21 @@ export async function initSubjects() {
renderSubjects(allSubjects);
populateSubjectSelect(allSubjects);
} catch (e) {
if (subjectsTbody) subjectsTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Ошибка загрузки</td></tr>';
if (subjectsTbody) subjectsTbody.innerHTML = '<tr><td colspan="5" class="loading-row">Ошибка загрузки</td></tr>';
}
}
function renderSubjects(subjects) {
if (!subjects || !subjects.length) {
subjectsTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Нет дисциплин</td></tr>';
subjectsTbody.innerHTML = '<tr><td colspan="5" class="loading-row">Нет дисциплин</td></tr>';
return;
}
subjectsTbody.innerHTML = subjects.map(s => `
<tr>
<td>${s.id}</td>
<td>${escapeHtml(s.name)}</td>
<td>${escapeHtml(s.code || '-')}</td>
<td>${s.departmentId || '-'}</td>
<td><button class="btn-delete" data-id="${s.id}">Удалить</button></td>
</tr>`).join('');
}
@@ -100,11 +102,19 @@ export async function initSubjects() {
e.preventDefault();
hideAlert('create-subject-alert');
const name = document.getElementById('new-subject-name').value.trim();
const code = document.getElementById('new-subject-code').value.trim();
const departmentId = document.getElementById('new-subject-department').value;
if (!name) { showAlert('create-subject-alert', 'Введите название', 'error'); return; }
if (!code) { showAlert('create-subject-alert', 'Введите код предмета', 'error'); return; }
if (!departmentId) { showAlert('create-subject-alert', 'Введите идентификатор кафедры', 'error'); return; }
try {
const data = await api.post('/api/subjects', { name });
showAlert('create-subject-alert', `Дисциплина "${escapeHtml(data.name)}" добавлена`, 'success');
const data = await api.post('/api/subjects', {
name,
code,
departmentId: Number(departmentId)
});
showAlert('create-subject-alert', `Дисциплина "${escapeHtml(data.name || name)}" добавлена`, 'success');
createSubjectForm.reset();
loadSubjects();
} catch (e) { showAlert('create-subject-alert', e.message || 'Ошибка создания', 'error'); }

View File

@@ -0,0 +1,193 @@
<div class="card">
<h2>Кафедра</h2>
<div class="filter-row" style="gap:.75rem;">
<label for="recordsSearch">Поиск</label>
<input
id="recordsSearch"
class="records-search"
type="search"
placeholder="Группа, дисциплина, преподаватель…"
autocomplete="off"
/>
<button type="button" class="btn-delete" id="recordsSearchClear">Сброс</button>
</div>
</div>
<div class="table-wrap">
<!-- Таблица 1 -->
<details class="table-item">
<summary>
<div class="chev" aria-hidden="true">
<svg viewBox="0 0 20 20" class="chev-icon" focusable="false" aria-hidden="true">
<path d="M5.5 7.5L10 12l4.5-4.5" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="title title-multiline">
<span class="title-main">Данные к составлению расписания</span>
<span class="title-sub">Кафедра: <b>Информационная безопасность</b></span>
<span class="title-sub">Факультет: <b>ФиПИ</b></span>
<span class="title-sub">Семестр: <b>весенний</b></span>
<span class="title-sub">Уч. год: <b>2024/2025</b></span>
</div>
<div class="meta">3 записи</div>
</summary>
<div class="content">
<table>
<thead>
<tr>
<th>Специальность</th>
<th>Курс и семестр</th>
<th>Группа</th>
<th>Дисциплина</th>
<th>Вид занятий</th>
<th>Часов в неделю</th>
<th>Деление на подгруппы</th>
<th>Фамилия преподавателя</th>
</tr>
</thead>
<tbody>
<!-- 1 строка = 1 запись HARDCODE -->
<tr>
<td>09.02.07</td>
<td>2 курс, 4 семестр</td>
<td>ИС-21</td>
<td>Базы данных</td>
<td>Лабораторная</td>
<td>2</td>
<td>Да</td>
<td>Иванов</td>
</tr>
<tr>
<td>09.02.07</td>
<td>2 курс, 4 семестр</td>
<td>ИС-22</td>
<td>Операционные системы</td>
<td>Практика</td>
<td>1</td>
<td>Нет</td>
<td>Смирнов</td>
</tr>
<tr>
<td>09.02.07</td>
<td>1 курс, 2 семестр</td>
<td>ИС-12</td>
<td>Алгоритмы</td>
<td>Лекция</td>
<td>2</td>
<td>Нет</td>
<td>Кузнецов</td>
</tr>
</tbody>
</table>
</div>
</details>
<!-- Таблица 2 -->
<details class="table-item">
<summary>
<div class="chev" aria-hidden="true">
<svg viewBox="0 0 20 20" class="chev-icon" focusable="false" aria-hidden="true">
<path d="M5.5 7.5L10 12l4.5-4.5" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="title">orders</div>
<div class="meta">1 запись</div>
</summary>
<div class="content">
<table>
<thead>
<tr>
<th>Специальность</th>
<th>Курс и семестр</th>
<th>Группа</th>
<th>Дисциплина</th>
<th>Вид занятий</th>
<th>Часов в неделю</th>
<th>Деление на подгруппы</th>
<th>Фамилия преподавателя</th>
</tr>
</thead>
<tbody>
<tr>
<td>38.02.01</td>
<td>1 курс, 1 семестр</td>
<td>ЭК-11</td>
<td>Экономика</td>
<td>Лекция</td>
<td>1</td>
<td>Нет</td>
<td>Петров</td>
</tr>
</tbody>
</table>
</div>
</details>
<!-- Таблица 3 -->
<details class="table-item">
<summary>
<div class="chev" aria-hidden="true">
<svg viewBox="0 0 20 20" class="chev-icon" focusable="false" aria-hidden="true">
<path d="M5.5 7.5L10 12l4.5-4.5" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="title">products</div>
<div class="meta">2 записи</div>
</summary>
<div class="content">
<table>
<thead>
<tr>
<th>Специальность</th>
<th>Курс и семестр</th>
<th>Группа</th>
<th>Дисциплина</th>
<th>Вид занятий</th>
<th>Часов в неделю</th>
<th>Деление на подгруппы</th>
<th>Фамилия преподавателя</th>
</tr>
</thead>
<tbody>
<tr>
<td>15.02.08</td>
<td>3 курс, 6 семестр</td>
<td>МС-31</td>
<td>Материаловедение</td>
<td>Практика</td>
<td>3</td>
<td>Да</td>
<td>Сидоров</td>
</tr>
<tr>
<td>15.02.08</td>
<td>3 курс, 6 семестр</td>
<td>МС-32</td>
<td>Технология металлов</td>
<td>Лабораторная</td>
<td>2</td>
<td>Да</td>
<td>Орлов</td>
</tr>
</tbody>
</table>
</div>
</details>
</div>
</div>

View File

@@ -7,6 +7,14 @@
<label for="new-subject-name">Название дисциплины</label>
<input type="text" id="new-subject-name" placeholder="Высшая математика" required>
</div>
<div class="form-group">
<label for="new-subject-code">Код предмета</label>
<input type="text" id="new-subject-code" placeholder="Например: MATH101" required>
</div>
<div class="form-group">
<label for="new-subject-department">Идентификатор кафедры</label>
<input type="number" id="new-subject-department" placeholder="ID кафедры" required>
</div>
<button type="submit" class="btn-primary">Добавить</button>
</div>
<div class="form-alert" id="create-subject-alert" role="alert"></div>
@@ -43,12 +51,14 @@
<tr>
<th>ID</th>
<th>Название</th>
<th>Код предмета</th>
<th>Кафедра (ID)</th>
<th>Действия</th>
</tr>
</thead>
<tbody id="subjects-tbody">
<tr>
<td colspan="3" class="loading-row">Загрузка...</td>
<td colspan="5" class="loading-row">Загрузка...</td>
</tr>
</tbody>
</table>