更新:models/routes/services/templates/docs
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
<option value="true" selected>进行中</option>
|
||||
<option value="false">已结束</option>
|
||||
</select>
|
||||
<button class="btn btn-primary btn-sm active" id="mineFilterBtn" onclick="toggleMineFilter()">
|
||||
<i class="bi bi-person"></i> 我的
|
||||
</button>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="addClassBtn" style="display:none;">
|
||||
<i class="bi bi-plus-circle"></i> 新增班级
|
||||
@@ -24,6 +27,7 @@
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>班级名称</th>
|
||||
<th>级别</th>
|
||||
<th>描述</th>
|
||||
<th>进行中</th>
|
||||
<th>学员数</th>
|
||||
@@ -64,10 +68,26 @@
|
||||
<label class="form-label">班级名称</label>
|
||||
<input type="text" class="form-control" id="className" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">班主任</label>
|
||||
<select class="form-select" id="classTeacher">
|
||||
<option value="">未指定</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">描述</label>
|
||||
<textarea class="form-control" id="classDesc" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">级别</label>
|
||||
<select class="form-select" id="classLevel">
|
||||
<option value="启蒙">启蒙</option>
|
||||
<option value="启蒙" selected>启蒙</option>
|
||||
<option value="进阶">进阶</option>
|
||||
<option value="熟练">熟练</option>
|
||||
<option value="精通">精通</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="classActive" checked>
|
||||
<label class="form-check-label" for="classActive">进行中</label>
|
||||
@@ -165,10 +185,29 @@ window.pageInit = function(data) {
|
||||
loadClasses();
|
||||
};
|
||||
|
||||
// 我的班级筛选
|
||||
function toggleMineFilter() {
|
||||
const btn = document.getElementById('mineFilterBtn');
|
||||
btn.classList.toggle('active');
|
||||
if (btn.classList.contains('active')) {
|
||||
btn.classList.remove('btn-outline-secondary');
|
||||
btn.classList.add('btn-primary');
|
||||
} else {
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-outline-secondary');
|
||||
}
|
||||
loadClasses();
|
||||
}
|
||||
|
||||
// 加载班级列表
|
||||
function loadClasses() {
|
||||
const activeFilter = document.getElementById('activeFilter').value;
|
||||
const url = activeFilter ? '/api/classes?active=' + activeFilter : '/api/classes';
|
||||
const mineFilter = document.getElementById('mineFilterBtn').classList.contains('active');
|
||||
let url = '/api/classes?';
|
||||
if (activeFilter) url += 'active=' + activeFilter + '&';
|
||||
if (mineFilter) url += 'mine=true&';
|
||||
url = url.endsWith('&') ? url.slice(0, -1) : url;
|
||||
url = url.endsWith('?') ? '/api/classes' : url;
|
||||
fetch(url).then(r => r.json()).then(classes => {
|
||||
const tbody = document.querySelector('#classesTable tbody');
|
||||
const isAdmin = currentUserRole === 'admin';
|
||||
@@ -176,13 +215,14 @@ function loadClasses() {
|
||||
<tr>
|
||||
<td>${c.id}</td>
|
||||
<td>${c.name}</td>
|
||||
<td>${c.level || '启蒙'}</td>
|
||||
<td>${c.description || '-'}</td>
|
||||
<td>${c.active ? '<span class="badge bg-success">进行中</span>' : '<span class="badge bg-secondary">已结束</span>'}</td>
|
||||
<td><a href="#" onclick="viewClassStudents(${c.id})">${c.student_count}</a></td>
|
||||
<td>${c.created_at}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-success me-1" onclick="openAssignGoalModal(${c.id}, '${c.name}')">分配目标</button>
|
||||
${isAdmin ? `<button type="button" class="btn btn-sm btn-primary me-1" onclick="editClass(${c.id}, '${c.name}', '${c.description || ''}', ${c.active})">编辑</button>
|
||||
${isAdmin ? `<button type="button" class="btn btn-sm btn-primary me-1" onclick="editClass(${c.id}, '${c.name}', ${c.teacher_id || 'null'}, '${c.description || ''}', ${c.active}, '${c.level || '启蒙'}')">编辑</button>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="deleteClass(${c.id})">删除</button>` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -194,7 +234,9 @@ function loadClasses() {
|
||||
document.getElementById('saveClassBtn').onclick = () => {
|
||||
const id = document.getElementById('classId').value;
|
||||
const name = document.getElementById('className').value.trim();
|
||||
const teacherId = document.getElementById('classTeacher').value;
|
||||
const description = document.getElementById('classDesc').value;
|
||||
const level = document.getElementById('classLevel').value;
|
||||
const active = document.getElementById('classActive').checked;
|
||||
|
||||
if (!name) {
|
||||
@@ -206,7 +248,7 @@ document.getElementById('saveClassBtn').onclick = () => {
|
||||
fetch('/api/classes' + (id ? '/' + id : ''), {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, description, active })
|
||||
body: JSON.stringify({ name, description, level, active, teacher_id: teacherId ? parseInt(teacherId) : null })
|
||||
}).then(r => r.json()).then(data => {
|
||||
if (data.error) {
|
||||
showToast(data.error);
|
||||
@@ -221,12 +263,14 @@ document.getElementById('saveClassBtn').onclick = () => {
|
||||
};
|
||||
|
||||
// 编辑班级
|
||||
function editClass(id, name, desc, active) {
|
||||
function editClass(id, name, teacherId, desc, active, level) {
|
||||
document.getElementById('classId').value = id;
|
||||
document.getElementById('className').value = name;
|
||||
document.getElementById('classDesc').value = desc;
|
||||
document.getElementById('classActive').checked = active !== false;
|
||||
document.getElementById('classLevel').value = level || '启蒙';
|
||||
document.getElementById('classModalTitle').textContent = '编辑班级';
|
||||
loadTeacherOptions(teacherId);
|
||||
new bootstrap.Modal(document.getElementById('classModal')).show();
|
||||
}
|
||||
|
||||
@@ -289,6 +333,18 @@ document.getElementById('confirmAssignBtn').onclick = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 加载班主任选项
|
||||
function loadTeacherOptions(selectedId) {
|
||||
fetch('/api/teachers').then(r => r.json()).then(users => {
|
||||
const select = document.getElementById('classTeacher');
|
||||
select.innerHTML = '<option value="">未指定</option>';
|
||||
users.forEach(u => {
|
||||
const selected = u.id === selectedId ? 'selected' : '';
|
||||
select.innerHTML += `<option value="${u.id}" ${selected}>${u.name}</option>`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 新增班级按钮
|
||||
document.getElementById('addClassBtn').onclick = () => {
|
||||
document.getElementById('classId').value = '';
|
||||
@@ -296,6 +352,7 @@ document.getElementById('addClassBtn').onclick = () => {
|
||||
document.getElementById('classDesc').value = '';
|
||||
document.getElementById('classActive').checked = true;
|
||||
document.getElementById('classModalTitle').textContent = '新增班级';
|
||||
loadTeacherOptions(null);
|
||||
new bootstrap.Modal(document.getElementById('classModal')).show();
|
||||
};
|
||||
|
||||
@@ -348,6 +405,22 @@ document.getElementById('assign-assessment-date').addEventListener('change', fun
|
||||
}
|
||||
});
|
||||
|
||||
// 开始日期联动:修改开始日期后,如果使用"XX天后"评估,自动重新计算评估日期
|
||||
document.getElementById('assign-start-date').addEventListener('change', function() {
|
||||
const startDateStr = this.value;
|
||||
const daysStr = document.getElementById('assign-assessment-days').value;
|
||||
if (startDateStr && daysStr) {
|
||||
const days = parseInt(daysStr);
|
||||
const [y, m, d] = startDateStr.split('-').map(Number);
|
||||
const startDate = new Date(y, m - 1, d);
|
||||
startDate.setDate(startDate.getDate() + days);
|
||||
const yy = startDate.getFullYear();
|
||||
const mm = String(startDate.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(startDate.getDate()).padStart(2, '0');
|
||||
document.getElementById('assign-assessment-date').value = `${yy}-${mm}-${dd}`;
|
||||
}
|
||||
});
|
||||
|
||||
// 确认分配目标
|
||||
document.getElementById('confirm-assign-goal').addEventListener('click', async () => {
|
||||
const goalId = document.getElementById('assign-goal-select').value;
|
||||
|
||||
Reference in New Issue
Block a user