feat: v1.4.0 - 典型方案采纳、推荐方案列表、审计字段、导航优化

- 添加典型方案采纳功能 (POST /api/plans/<id>/adopt)
- 添加推荐方案列表 (GET /api/students/<id>/recommended-plans)
- PracticePlan 新增 created_by/updated_by/updated_at 审计字段
- 方案编辑/详情页导航优化 (bfcache 处理、pageshow 事件)
- 方案列表支持删除功能
- 学员列表'暂无方案/问题'样式统一
- 更新文档:问题文件已废弃(迁移到数据库)
- 更新部署脚本和验证清单
This commit is contained in:
hmo
2026-04-27 02:01:22 +08:00
parent 6abdd49c04
commit e50a9207b4
20 changed files with 873 additions and 88 deletions
+93 -3
View File
@@ -75,18 +75,86 @@
<script>
// 防抖定时器
let debounceTimer = null;
const STORAGE_KEY = 'plans_filters';
function debounceLoad() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(loadPlans, 300);
}
// 保存筛选状态到 sessionStorage
function saveFilterState() {
const state = {
classId: document.getElementById('filterClass').value,
templateId: document.getElementById('filterTemplate').value,
isTypical: document.getElementById('filterTypical').value,
studentName: document.getElementById('filterStudentName').value,
problemId: document.getElementById('filterProblem').value,
mineActive: document.getElementById('minePlansBtn')?.classList.contains('active')
};
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
// 恢复筛选状态
function restoreFilterState() {
const saved = sessionStorage.getItem(STORAGE_KEY);
if (!saved) return;
try {
const state = JSON.parse(saved);
if (state.classId) document.getElementById('filterClass').value = state.classId;
if (state.templateId) document.getElementById('filterTemplate').value = state.templateId;
if (state.isTypical) document.getElementById('filterTypical').value = state.isTypical;
if (state.studentName) document.getElementById('filterStudentName').value = state.studentName;
if (state.problemId) document.getElementById('filterProblem').value = state.problemId;
const btn = document.getElementById('minePlansBtn');
if (btn) {
if (state.mineActive) {
btn.classList.add('active', 'btn-primary');
btn.classList.remove('btn-outline-secondary');
} else {
btn.classList.remove('active', 'btn-primary');
btn.classList.add('btn-outline-secondary');
}
}
// 确保状态同步保存
saveFilterState();
} catch (e) {
console.error('恢复筛选状态失败', e);
}
}
// 页面初始化
window.pageInit = function() {
loadFilters();
loadPlans();
checkAndRefresh();
};
// 检查是否需要刷新(从详情页返回)
function checkAndRefresh() {
const needsRefresh = sessionStorage.getItem('plans_needs_refresh') === 'true';
if (needsRefresh) {
sessionStorage.removeItem('plans_needs_refresh');
loadFilters().then(() => {
restoreFilterState();
loadPlans(true);
});
} else {
loadFilters().then(() => {
restoreFilterState();
loadPlans(false);
});
}
}
// pageshow 事件处理 bfcache 恢复的情况
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
// 页面从 bfcache 恢复,需要重新检查刷新标记
checkAndRefresh();
}
});
// 加载筛选器选项
async function loadFilters() {
// 加载班级
@@ -107,7 +175,7 @@ async function loadFilters() {
const problems = await resp.json();
const problemSelect = document.getElementById('filterProblem');
problems.forEach(p => {
problemSelect.innerHTML += `<option value="${p.id}">${p.name}</option>`;
problemSelect.innerHTML += `<option value="${p.id}">${p.no} - ${p.name}</option>`;
});
} catch (e) {
console.error('加载问题失败', e);
@@ -128,6 +196,9 @@ async function loadFilters() {
// 加载方案列表
async function loadPlans() {
// 保存当前筛选状态
saveFilterState();
const container = document.getElementById('plansContainer');
container.innerHTML = '<div class="text-center text-muted py-5"><i class="bi bi-hourglass fs-4"></i><p class="mt-2">加载中...</p></div>';
@@ -199,6 +270,7 @@ async function loadPlans() {
<td class="text-muted small">${p.created_at || ''}</td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewPlan(${p.id})">查看</button>
<button class="btn btn-sm btn-outline-danger" onclick="deletePlan(${p.id})">删除</button>
</td>
</tr>
`;
@@ -224,6 +296,7 @@ function clearFilters() {
mineBtn.classList.remove('active', 'btn-primary');
mineBtn.classList.add('btn-outline-secondary');
}
sessionStorage.removeItem(STORAGE_KEY);
loadPlans();
}
@@ -239,6 +312,7 @@ function toggleMinePlans() {
btn.classList.remove('btn-primary');
btn.classList.add('btn-outline-secondary');
}
saveFilterState();
loadPlans();
}
@@ -246,5 +320,21 @@ function toggleMinePlans() {
function viewPlan(planId) {
window.location.href = `/plan/${planId}`;
}
// 删除方案
async function deletePlan(planId) {
if (!confirm('确定删除该方案?删除后无法恢复。')) return;
try {
const resp = await fetch(`/api/plans/${planId}`, { method: 'DELETE' });
if (resp.ok) {
loadPlans(); // 刷新列表
} else {
const err = await resp.json();
alert('删除失败: ' + (err.error || '未知错误'));
}
} catch (e) {
alert('删除失败: ' + e.message);
}
}
</script>
{% endblock %}