Предмет
This commit is contained in:
@@ -39,9 +39,36 @@ h2 { margin: 15px 0 8px; font-size: 16px; }
|
||||
max-width: 300px;
|
||||
}
|
||||
.input-sm { max-width: 120px; }
|
||||
.input-xs { max-width: 80px; }
|
||||
|
||||
#subject-select { max-width: 220px; }
|
||||
|
||||
.search-bar { margin-bottom: 15px; }
|
||||
|
||||
.filter-extras {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.filter-extras .form-row {
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.filter-extras .form-row label {
|
||||
min-width: auto;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.filter-extras .form-row label:not(:first-child) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@@ -98,9 +98,19 @@ router.post('/import', upload.single('xlsfile'), (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/subjects', (req, res) => {
|
||||
const rows = dbAll('SELECT code, name FROM subjects ORDER BY is_mandatory DESC, name');
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
router.get('/students', (req, res) => {
|
||||
const search = req.query.search || '';
|
||||
const filter = req.query.filter || '';
|
||||
const subjectCode = req.query.subject_code || '';
|
||||
const scoreMin = req.query.score_min;
|
||||
const scoreMax = req.query.score_max;
|
||||
const gradeMin = req.query.grade_min;
|
||||
const gradeMax = req.query.grade_max;
|
||||
|
||||
const baseQuery = `
|
||||
SELECT s.*,
|
||||
@@ -125,6 +135,27 @@ router.get('/students', (req, res) => {
|
||||
params.push(like, like, like, like);
|
||||
}
|
||||
|
||||
if (subjectCode) {
|
||||
whereClauses.push('er.subject_code = ?');
|
||||
params.push(subjectCode);
|
||||
}
|
||||
if (scoreMin !== undefined && scoreMin !== '') {
|
||||
whereClauses.push('er.primary_score >= ?');
|
||||
params.push(parseInt(scoreMin, 10));
|
||||
}
|
||||
if (scoreMax !== undefined && scoreMax !== '') {
|
||||
whereClauses.push('er.primary_score <= ?');
|
||||
params.push(parseInt(scoreMax, 10));
|
||||
}
|
||||
if (gradeMin !== undefined && gradeMin !== '') {
|
||||
whereClauses.push('er.grade >= ?');
|
||||
params.push(parseInt(gradeMin, 10));
|
||||
}
|
||||
if (gradeMax !== undefined && gradeMax !== '') {
|
||||
whereClauses.push('er.grade <= ?');
|
||||
params.push(parseInt(gradeMax, 10));
|
||||
}
|
||||
|
||||
let havingClause = '';
|
||||
if (filter === 'gia' || filter === 'oge') {
|
||||
havingClause = 'HAVING elective_count > 0';
|
||||
|
||||
@@ -72,6 +72,21 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="filter-extras">
|
||||
<div class="form-row">
|
||||
<label>Предмет:</label>
|
||||
<select id="subject-select" class="input" onchange="loadStudents()">
|
||||
<option value="">Все предметы</option>
|
||||
</select>
|
||||
<label>Балл:</label>
|
||||
<input type="number" id="score-min" class="input input-xs" placeholder="от" oninput="loadStudents()">
|
||||
<input type="number" id="score-max" class="input input-xs" placeholder="до" oninput="loadStudents()">
|
||||
<label>Оценка:</label>
|
||||
<input type="number" id="grade-min" class="input input-xs" placeholder="от" min="2" max="5" oninput="loadStudents()">
|
||||
<input type="number" id="grade-max" class="input input-xs" placeholder="до" min="2" max="5" oninput="loadStudents()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search" class="input" placeholder="Поиск по ФИО или классу...">
|
||||
</div>
|
||||
@@ -125,9 +140,19 @@
|
||||
async function loadStudents() {
|
||||
const search = document.getElementById('search').value;
|
||||
const filter = document.querySelector('input[name="filter"]:checked').value;
|
||||
const subjectCode = document.getElementById('subject-select').value;
|
||||
const scoreMin = document.getElementById('score-min').value;
|
||||
const scoreMax = document.getElementById('score-max').value;
|
||||
const gradeMin = document.getElementById('grade-min').value;
|
||||
const gradeMax = document.getElementById('grade-max').value;
|
||||
const params = new URLSearchParams();
|
||||
if (search) params.set('search', search);
|
||||
if (filter && filter !== 'all') params.set('filter', filter);
|
||||
if (subjectCode) params.set('subject_code', subjectCode);
|
||||
if (scoreMin) params.set('score_min', scoreMin);
|
||||
if (scoreMax) params.set('score_max', scoreMax);
|
||||
if (gradeMin) params.set('grade_min', gradeMin);
|
||||
if (gradeMax) params.set('grade_max', gradeMax);
|
||||
const url = '/api/students' + (params.toString() ? '?' + params.toString() : '');
|
||||
const res = await fetch(url);
|
||||
allStudents = await res.json();
|
||||
@@ -145,6 +170,14 @@
|
||||
parSel.innerHTML = parallels.map(p => `<option value="${esc(p)}">${esc(p)}-е классы</option>`).join('');
|
||||
}
|
||||
|
||||
async function loadSubjects() {
|
||||
const res = await fetch('/api/subjects');
|
||||
const subjects = await res.json();
|
||||
const sel = document.getElementById('subject-select');
|
||||
sel.innerHTML = '<option value="">Все предметы</option>' +
|
||||
subjects.map(s => `<option value="${esc(s.code)}">${esc(s.name)}</option>`).join('');
|
||||
}
|
||||
|
||||
function toggleBatchMode() {
|
||||
const scope = document.querySelector('input[name="batch-scope"]:checked').value;
|
||||
document.getElementById('class-select').disabled = scope !== 'class';
|
||||
@@ -236,6 +269,7 @@
|
||||
|
||||
loadStudents();
|
||||
loadClasses();
|
||||
loadSubjects();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user