📖 课程目录 - ${currentCourseName}
${showMismatchWarning ? `
⚠️ 缓存的课程与当前课程不匹配,建议重新读取
` : ''}
勾选要执行的任务,然后点击「⚡ 批量执行」按钮开始。
${isFromCache ? `(缓存于 ${cacheTime})` : ''}
${tasksHtml}
已选择: 0 个任务
`;
bindTaskListEvents(overlay, currentCourseName, availableTasks);
};
// 从页面刷新读取目录(以页面为准,清理错误的本地记录)
const refreshDirectory = async () => {
showLoading();
// 等待展开所有目录
await expandAllCategories();
// 使用页面真实状态,忽略本地记录
const tasks = getCourseTaskList({ ignoreLocalCompleted: true });
const availableTasks = tasks.filter(t => !t.isDisabled);
// 清理本地记录中与页面状态不一致的任务
const completed = loadBatchCompleted();
const ourCompletedTasks = completed[currentCourseName] || [];
if (ourCompletedTasks.length > 0) {
const pageCompletedIds = tasks.filter(t => t.isCompleted).map(t => t.id);
const tasksToRemove = ourCompletedTasks.filter(id => !pageCompletedIds.includes(id));
if (tasksToRemove.length > 0) {
completed[currentCourseName] = ourCompletedTasks.filter(id => !tasksToRemove.includes(id));
if (completed[currentCourseName].length === 0) {
delete completed[currentCourseName];
}
saveBatchCompleted(completed);
console.log('[WeLearn-Go] 清理了本地完成记录中的错误任务:', tasksToRemove);
}
}
// 保存到缓存
saveCourseDirectoryCache(currentCourseId, currentCourseName, availableTasks);
renderTaskList(availableTasks, false, false);
showToast(`已读取 ${availableTasks.length} 个任务`, { duration: 2000 });
};
// 刷新完成状态(从页面重新扫描任务状态,以页面为准)
const refreshCompletionStatus = async (cachedTasks) => {
showLoading();
// 重新扫描页面获取最新任务状态(忽略本地记录,只看页面真实状态)
const freshTasks = getCourseTaskList({ ignoreLocalCompleted: true });
const freshTaskMap = new Map(freshTasks.map(t => [t.id, t]));
// 加载本地完成记录
const completed = loadBatchCompleted();
const ourCompletedTasks = completed[currentCourseName] || [];
// 找出本地记录中标记完成但页面显示未完成的任务(需要清理)
const tasksToRemove = [];
// 更新任务的完成状态(只使用页面真实状态)
const updatedTasks = cachedTasks.map(task => {
const freshTask = freshTaskMap.get(task.id);
const pageCompleted = freshTask?.isCompleted || false;
// 如果本地记录说已完成,但页面显示未完成,需要清理
if (ourCompletedTasks.includes(task.id) && !pageCompleted) {
tasksToRemove.push(task.id);
}
return {
...task,
isCompleted: pageCompleted
};
});
// 清理本地记录中错误标记的任务
if (tasksToRemove.length > 0 && completed[currentCourseName]) {
completed[currentCourseName] = completed[currentCourseName].filter(id => !tasksToRemove.includes(id));
if (completed[currentCourseName].length === 0) {
delete completed[currentCourseName];
}
saveBatchCompleted(completed);
console.log('[WeLearn-Go] 清理了本地完成记录中的错误任务:', tasksToRemove);
}
// 更新缓存
saveCourseDirectoryCache(currentCourseId, currentCourseName, updatedTasks);
renderTaskList(updatedTasks, false, true);
// 统计完成数量(只计算页面真实状态)
const completedCount = updatedTasks.filter(t => t.isCompleted).length;
const cleanedMsg = tasksToRemove.length > 0 ? `,已清理 ${tasksToRemove.length} 条错误记录` : '';
showToast(`已刷新完成状态 (${completedCount}/${updatedTasks.length} 已完成)${cleanedMsg}`, { duration: 3000 });
};
document.body.appendChild(overlay);
// 点击遮罩关闭
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.remove();
}
});
// 如果强制刷新或没有缓存,直接读取
if (forceRefresh || !hasCacheForCurrentCourse) {
if (courseIdMismatch) {
// 课程不匹配,显示警告并读取
showLoading();
await refreshDirectory();
} else {
// 无缓存,直接读取
showLoading();
await refreshDirectory();
}
} else {
// 有缓存,先刷新完成状态
await refreshCompletionStatus(cache.tasks);
}
};
/** 绑定任务列表事件 */
const bindTaskListEvents = (overlay, courseName, availableTasks) => {
const taskCheckboxes = overlay.querySelectorAll('.welearn-task-checkbox:not([disabled])');
const unitCheckboxes = overlay.querySelectorAll('.welearn-unit-checkbox');
const selectedCountEl = overlay.querySelector('.welearn-selected-count');
const confirmButton = overlay.querySelector('.welearn-modal-confirm');
const cancelButton = overlay.querySelector('.welearn-modal-cancel');
const selectAllBtn = overlay.querySelector('.welearn-btn-select-all');
const deselectAllBtn = overlay.querySelector('.welearn-btn-deselect-all');
const refreshBtn = overlay.querySelector('.welearn-btn-refresh');
const refreshStatusBtn = overlay.querySelector('.welearn-btn-refresh-status');
/** 更新选中数量和按钮状态 */
const updateSelectionState = () => {
const checkedCount = overlay.querySelectorAll('.welearn-task-checkbox:checked').length;
selectedCountEl.textContent = checkedCount;
confirmButton.disabled = checkedCount === 0;
// 更新单元复选框状态
unitCheckboxes.forEach(unitCb => {
const unitContainer = unitCb.closest('.welearn-task-unit');
const unitTasks = unitContainer?.querySelectorAll('.welearn-task-checkbox:not([disabled])') || [];
const checkedInUnit = unitContainer?.querySelectorAll('.welearn-task-checkbox:checked').length || 0;
unitCb.checked = unitTasks.length > 0 && checkedInUnit === unitTasks.length;
unitCb.indeterminate = checkedInUnit > 0 && checkedInUnit < unitTasks.length;
});
};
// 初始化选中状态
updateSelectionState();
// 任务复选框事件
taskCheckboxes.forEach(cb => {
cb.addEventListener('change', updateSelectionState);
});
// 单元复选框事件
unitCheckboxes.forEach(unitCb => {
unitCb.addEventListener('change', () => {
const unitContainer = unitCb.closest('.welearn-task-unit');
const unitTasks = unitContainer?.querySelectorAll('.welearn-task-checkbox:not([disabled])') || [];
unitTasks.forEach(cb => {
cb.checked = unitCb.checked;
});
updateSelectionState();
});
});
// 全选按钮
selectAllBtn?.addEventListener('click', () => {
taskCheckboxes.forEach(cb => { cb.checked = true; });
updateSelectionState();
});
// 取消全选按钮
deselectAllBtn?.addEventListener('click', () => {
taskCheckboxes.forEach(cb => { cb.checked = false; });
updateSelectionState();
});
// 重新读取按钮
refreshBtn?.addEventListener('click', () => {
showTaskSelectorModal(true);
overlay.remove();
});
// 刷新完成状态按钮 - 重新扫描页面获取最新完成状态(以页面为准)
refreshStatusBtn?.addEventListener('click', async () => {
refreshStatusBtn.disabled = true;
refreshStatusBtn.textContent = '刷新中...';
// 保存当前选中的任务ID
const checkedTaskIds = [];
overlay.querySelectorAll('.welearn-task-checkbox:checked').forEach(cb => {
checkedTaskIds.push(cb.dataset.taskId);
});
// 重新扫描页面获取最新任务状态(忽略本地记录,只看页面真实状态)
const freshTasks = getCourseTaskList({ ignoreLocalCompleted: true });
const freshTaskMap = new Map(freshTasks.map(t => [t.id, t]));
// 加载本地完成记录
const completed = loadBatchCompleted();
const ourCompletedTasks = completed[courseName] || [];
// 找出本地记录中标记完成但页面显示未完成的任务(需要清理)
const tasksToRemove = [];
// 更新任务列表中的完成状态
overlay.querySelectorAll('.welearn-task-item').forEach(item => {
const checkbox = item.querySelector('.welearn-task-checkbox');
if (!checkbox) return;
const taskId = checkbox.dataset.taskId;
const freshTask = freshTaskMap.get(taskId);
// 页面真实的完成状态(不考虑本地记录)
const pageCompleted = freshTask?.isCompleted || false;
const wasCompleted = item.classList.contains('completed');
const wasInLocalRecord = ourCompletedTasks.includes(taskId);
// 如果本地记录说已完成,但页面显示未完成,需要清理本地记录
if (wasInLocalRecord && !pageCompleted) {
tasksToRemove.push(taskId);
}
if (pageCompleted && !wasCompleted) {
// 页面显示已完成
item.classList.add('completed');
checkbox.checked = false;
checkbox.disabled = true;
// 更新徽章为已完成(绿色)
let badge = item.querySelector('.welearn-task-badge');
if (!badge) {
badge = document.createElement('span');
item.appendChild(badge);
}
badge.className = 'welearn-task-badge';
badge.textContent = '✓ 已完成';
} else if (!pageCompleted && wasCompleted) {
// 页面显示未完成(之前可能是本地记录标记的)
item.classList.remove('completed');
checkbox.disabled = false;
// 更新徽章为待完成(黄色)
let badge = item.querySelector('.welearn-task-badge');
if (!badge) {
badge = document.createElement('span');
item.appendChild(badge);
}
badge.className = 'welearn-task-badge pending';
badge.textContent = '○ 待完成';
// 恢复之前的选中状态
if (checkedTaskIds.includes(taskId)) {
checkbox.checked = true;
}
}
});
// 清理本地记录中错误标记的任务
if (tasksToRemove.length > 0 && completed[courseName]) {
completed[courseName] = completed[courseName].filter(id => !tasksToRemove.includes(id));
if (completed[courseName].length === 0) {
delete completed[courseName];
}
saveBatchCompleted(completed);
console.log('[WeLearn-Go] 清理了本地完成记录中的错误任务:', tasksToRemove);
}
// 更新缓存中的任务状态(使用页面真实状态)
const currentCourseId = getCourseId();
const validTasks = freshTasks.filter(t => !t.isDisabled);
saveCourseDirectoryCache(currentCourseId, courseName, validTasks);
// 计算完成数量(只计算页面真实状态)
const completedCount = validTasks.filter(t => t.isCompleted).length;
updateSelectionState();
refreshStatusBtn.disabled = false;
refreshStatusBtn.textContent = '🔃 刷新完成状态';
const cleanedMsg = tasksToRemove.length > 0 ? `,已清理 ${tasksToRemove.length} 条错误记录` : '';
showToast(`已刷新完成状态 (${completedCount}/${validTasks.length} 已完成)${cleanedMsg}`, { duration: 3000 });
});
// 取消按钮
cancelButton?.addEventListener('click', () => {
overlay.remove();
});
// 确认选择按钮
confirmButton?.addEventListener('click', () => {
const tasks = [];
overlay.querySelectorAll('.welearn-task-checkbox:checked').forEach(cb => {
tasks.push({
id: cb.dataset.taskId,
title: cb.dataset.title
});
});
if (tasks.length === 0) {
showToast('请至少选择一个任务');
return;
}
// 保存选择的任务到全局变量和缓存
selectedBatchTasks = tasks;
selectedCourseName = courseName;
saveBatchTasksCache(courseName, tasks);
overlay.remove();
showToast(`已选择 ${tasks.length} 个任务,点击「⚡ 批量执行」开始`, { duration: 3000 });
updateBatchButtonState();
});
};
/** 显示恢复批量任务提示 */
const showBatchTasksRecoveryPrompt = () => {
const tasksCache = loadBatchTasksCache();
if (!tasksCache || !tasksCache.tasks || tasksCache.tasks.length === 0) return;
const currentCourseName = getCourseName();
const isSameCourse = tasksCache.courseName === currentCourseName;
const cacheTime = new Date(tasksCache.timestamp).toLocaleString('zh-CN');
const overlay = document.createElement('div');
overlay.className = 'welearn-modal-overlay welearn-recovery-prompt';
overlay.innerHTML = `