This commit is contained in:
2026-07-01 11:59:35 +05:00
parent 0bf4686b9a
commit 50e8265b74
3 changed files with 22 additions and 4 deletions

View File

@@ -6,7 +6,7 @@ body {
color: #333;
line-height: 1.5;
}
.container { max-width: 960px; margin: 0 auto; padding: 20px; }
.container { max-width: 100%; margin: 0 auto; padding: 20px; }
h1 { margin-bottom: 15px; }
h2 { margin: 15px 0 8px; font-size: 16px; }

View File

@@ -91,7 +91,9 @@ router.get('/students', (req, res) => {
SELECT s.*,
COUNT(er.id) as exam_count,
SUM(CASE WHEN s2.is_mandatory = 0 THEN 1 ELSE 0 END) as elective_count,
SUM(CASE WHEN s2.is_mandatory = 1 AND er.grade IS NOT NULL THEN 1 ELSE 0 END) as mandatory_grades
SUM(CASE WHEN s2.is_mandatory = 1 AND er.grade IS NOT NULL THEN 1 ELSE 0 END) as mandatory_grades,
SUM(CASE WHEN s2.is_mandatory = 1 AND er.grade IS NOT NULL AND er.grade < 3 THEN 1 ELSE 0 END) as low_grades,
GROUP_CONCAT(s2.name || ' ' || CAST(er.grade AS TEXT), ' / ') as subjects_list
FROM students s
LEFT JOIN exam_results er ON er.student_id = s.id
LEFT JOIN subjects s2 ON s2.code = er.subject_code
@@ -129,6 +131,8 @@ router.get('/students', (req, res) => {
)`);
} else if (filter === 'secondyear') {
havingClause = 'HAVING elective_count = 0 AND COUNT(er.id) = 1';
} else if (filter === 'lowgrade') {
havingClause = 'HAVING low_grades > 0';
}
const wherePart = whereClauses.length ? 'WHERE ' + whereClauses.join(' AND ') : '';

View File

@@ -63,6 +63,9 @@
<label class="filter-label" style="background:#fff0d0;color:#860">
<input type="radio" name="filter" value="secondyear" onchange="onFilterChange()"> Второгодники
</label>
<label class="filter-label" style="background:#fdd;color:#a00">
<input type="radio" name="filter" value="lowgrade" onchange="onFilterChange()"> Отметка &lt; 3
</label>
</div>
<div class="search-bar">
@@ -79,6 +82,7 @@
<th>Класс</th>
<th>Предметов</th>
<th>Тип</th>
<th>Предметы (оценка)</th>
<th>Действия</th>
</tr>
</thead>
@@ -154,6 +158,15 @@
}
}
function formatSubjectsList(list) {
if (!list) return '—';
return list.split(' / ').map(item => {
const m = item.match(/^(.*?)\s*(\d)\s*$/);
if (m && parseInt(m[2]) < 3) return `<span style="color:#c0392b;font-weight:600">${esc(item)}</span>`;
return esc(item);
}).join(' / ');
}
function renderTable(students) {
const tbody = document.querySelector('#students-table tbody');
tbody.innerHTML = students.map((s, i) => {
@@ -164,7 +177,7 @@
const examType = isOge ? 'ОГЭ' : 'ГВЭ';
const examTypeClass = isOge ? 'badge-gia' : 'badge-gve';
return `
<tr class="${rowClass}">
<tr class="${rowClass}" onclick="window.location.href='/student/${s.id}'" style="cursor:pointer">
<td>${i + 1}</td>
<td>${esc(s.last_name)}</td>
<td>${esc(s.first_name)}</td>
@@ -172,7 +185,8 @@
<td>${esc(s.class)}</td>
<td>${s.exam_count}</td>
<td><span class="badge ${examTypeClass}">${examType}</span></td>
<td><a href="/student/${s.id}" class="btn btn-sm">Открыть</a></td>
<td style="font-size:11px;max-width:250px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${esc(s.subjects_list || '')}">${formatSubjectsList(s.subjects_list)}</td>
<td onclick="event.stopPropagation()"><a href="/student/${s.id}" class="btn btn-sm">Открыть</a></td>
</tr>`;
}).join('');
}