// ==UserScript== // @name 山东省继续医学教育公需科目学习平台 - 清空课程进度(免费版) // @namespace https://github.com/ // @version 1.0 // @description 免费版本 - 仅支持清空课程进度功能。点击页面顶部悬浮按钮即可使用。 // @include *://*.sdcme.net.cn/* // @include *://*sdcme.net.cn* // @grant GM_addStyle // @require https://m.zhanyc.cn/jquery-2.2.4.min.js // @require https://m.zhanyc.cn/layerjs-gm-with-css.js // @run-at document-end // ==/UserScript== (function () { 'use strict'; const FREE = { // ======================== 清空课程进度核心方法 ======================== async clearCourseProgress() { if (location.href.includes('/play/index.html')) { this.alertMsg('请不要在视频页面操作该功能!'); return; } // 1. 引流提示 let proceed = await new Promise((resolve) => { layer.open({ type: 1, title: '温馨提示', content: '
脚本用于清理学习记录,清空后重新学习可解决进度异常的问题。

如需自动看视频的脚本可查看 http://doc.zhanyc.cn/pages/sdsjxyx/

请勿相信任何加速学习的脚本和代挂渠道,1倍速看课才能避免异常!!
', area: ['480px', '300px'], btn: ['知道了,继续清理'], yes: function(index) { layer.close(index); resolve(true); }, cancel: function() { resolve(true); } }); }); if (!proceed) return; let firstIndex; // 1. 加载弹窗 let loadingIndex = layer.open({ type: 1, title: '清空课程进度', content: '
请等待……
', area: ['400px', '150px'], btn: false }); this.tipsMsg2('正在获取课程列表...', 0); let courses; try { courses = await this.getCourses(); } catch (e) { layer.close(loadingIndex); this.alertMsg('获取课程列表失败:' + e.message); return; } layer.close(loadingIndex); if (!courses || courses.length === 0) { this.alertMsg('没有获取到任何课程'); return; } // 2. 课程选择对话框 this.tipsMsg2('正在显示课程列表...'); let selectedCourseIds = await new Promise((resolve) => { let html = '
'; html += '
脚本用于清理学习记录,清空后重新学习可解决进度异常的问题。
如需自动看视频的脚本可查看 http://doc.zhanyc.cn/pages/sdsjxyx/
请勿相信任何加速学习的脚本和代挂渠道,1倍速看课才能避免异常!!
'; html += '
'; html += ''; html += ' '; html += ''; html += ' '; html += ''; html += '
'; courses.forEach(course => { html += `
`; }); html += '
'; firstIndex = layer.open({ type: 1, title: '选择需要清空进度的课程', content: html, area: ['500px', '500px'], btn: ['确认清空', '取消'], yes: function (index) { let ids = []; $('.zfk-clear-course-cb:checked').each(function () { ids.push($(this).val()); }); if (ids.length === 0) { FREE.tipsMsg('请至少选择一个课程'); return false; } layer.open({ type: 1, title: '确认清空', content: `
是否确认清空选中的${ids.length}个课程的所有章节进度?
`, btn: ['确认', '取消'], yes: function (confirmIndex) { layer.close(confirmIndex); layer.close(index); resolve(ids); }, btn2: function (confirmIndex) { layer.close(confirmIndex); } }); return false; }, btn2: function (index) { layer.close(index); resolve([]); } }); }); if (selectedCourseIds.length === 0) return; // 3. 获取所有选中课程的章节列表 this.tipsMsg2('正在获取章节列表...', 0); let allChapters = []; let selectedCourses = courses.filter(c => selectedCourseIds.includes(String(c.id))); let chapterPromises = selectedCourses.map(async (course) => { try { let chapters = await this.getChapters(course.id); chapters.forEach(ch => { ch.courseTitle = course.title; }); return chapters; } catch (e) { this.tipsMsg2('获取课程【' + course.title + '】章节列表失败:' + e.message); return []; } }); let chaptersResults = await Promise.all(chapterPromises); allChapters = chaptersResults.flat(); layer.closeAll(); if (allChapters.length === 0) { this.alertMsg('没有获取到任何章节信息'); return; } // 4. 进度面板 this.tipsMsg2('正在清空课程进度...', 0); let total = allChapters.length; let successCount = 0; let failCount = 0; let completedCount = 0; let groupHtml = ''; let lastCourseTitle = ''; allChapters.forEach((chapter, idx) => { if (chapter.courseTitle !== lastCourseTitle) { if (lastCourseTitle) groupHtml += ''; groupHtml += '
' + chapter.courseTitle + '
'; lastCourseTitle = chapter.courseTitle; } groupHtml += '
' + chapter.title + ' 等待处理...
'; }); if (lastCourseTitle) groupHtml += '
'; let panelHtml = '
' + '
概览数据:
' + '
脚本用于清理学习记录,清空后重新学习可解决进度异常的问题。
如需自动看视频的脚本可查看 http://doc.zhanyc.cn/pages/sdsjxyx/
请勿相信任何加速学习的脚本和代挂渠道,1倍速看课才能避免异常!!
' + '
' + '进度:0/' + total + ' | 成功:0 | 失败:0
' + '
课程数据:
' + '
' + groupHtml + '
' + '
' + '' + '
'; layer.close(firstIndex); let clearLayerIndex = layer.open({ type: 1, title: '清空课程进度', content: panelHtml, area: ['620px', '650px'], closeBtn: true, btn: false }); $('#zfk-clear-close-btn').on('click', function () { layer.close(clearLayerIndex); }); // 5. 并发清空 let maxConcurrency = Number($('#zfk-clear-thread-count').val()) || 5; async function processOneChapter(chapter, idx) { let resultMsg = ''; let isSuccess = false; try { let res = await FREE.resetChapter(chapter.fileName); if (res.code === 200 && res.msg === '该视频进度重置成功,请重新进入该课件进行学习') { successCount++; resultMsg = '成功'; isSuccess = true; } else { failCount++; resultMsg = res.msg || '失败'; } } catch (e) { failCount++; resultMsg = '请求失败'; } completedCount++; $('#zfk-clear-progress-overview').html('进度:' + completedCount + '/' + total + ' | 成功:' + successCount + ' | 失败:' + failCount); let color = isSuccess ? '#28a745' : '#dc3545'; $('#zfk-chapter-' + idx + ' span').html('' + resultMsg + ''); } let chapterIndex = 0; async function worker() { while (chapterIndex < allChapters.length) { let i = chapterIndex++; await processOneChapter(allChapters[i], i); } } let workers = []; let workerCount = Math.min(maxConcurrency, allChapters.length); for (let w = 0; w < workerCount; w++) { workers.push(worker()); } await Promise.all(workers); this.tipsMsg2('清空完成:成功 ' + successCount + ' 个,失败 ' + failCount + ' 个'); }, async getCourses() { let res = await fetch("https://course.sdcme.net.cn/courses", { headers: { "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9", "content-type": "application/json", "x-requested-with": "XMLHttpRequest" }, referrer: "https://course.sdcme.net.cn/", method: "GET", mode: "cors", credentials: "include" }); let data = await res.json(); if (data.code === 200 && data.success) { return data.data; } throw new Error(data.msg || "获取课程列表失败"); }, async getChapters(courseId) { let res = await fetch("https://course.sdcme.net.cn/coursewares/course/" + courseId, { headers: { "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9", "content-type": "application/json", "x-requested-with": "XMLHttpRequest" }, referrer: "https://course.sdcme.net.cn/course/" + courseId, method: "GET", mode: "cors", credentials: "include" }); let data = await res.json(); if (data.code === 200 && data.success) { return data.data; } throw new Error(data.msg || "获取章节列表失败"); }, async resetChapter(videoId) { let res = await fetch("https://course.sdcme.net.cn/api/rewatch", { headers: { "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9", "content-type": "application/json", "x-requested-with": "XMLHttpRequest" }, referrer: "https://course.sdcme.net.cn/", body: JSON.stringify({ videoId: videoId }), method: "POST", mode: "cors", credentials: "include" }); return await res.json(); }, // ======================== UI 工具方法 ======================== alertMsg(msg) { layer.open({ type: "1", content: '
' + msg + '
', title: "提示", offset: "100px", btn: "关闭" }); }, tipsMsg(msg, timeout) { layer.msg(msg, { offset: "100px", time: timeout || 3000 }); }, tipsMsg2(msg, timeout) { layer.msg(msg, { offset: "100px", time: timeout || 3000, icon: 0 }); }, // ======================== 初始化 ======================== init() { // 添加悬浮按钮样式 GM_addStyle(` #free-clear-btn { position: fixed; top: 10px; left: 10px; z-index: 999999; padding: 12px 24px; background: linear-gradient(135deg, #ff6b6b, #ee5a24); color: #fff; font-size: 16px; font-weight: bold; border: 2px solid #fff; border-radius: 8px; cursor: pointer; box-shadow: 0 4px 15px rgba(238, 90, 36, 0.4); transition: all 0.3s ease; font-family: "Microsoft YaHei", sans-serif; } #free-clear-btn:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(238, 90, 36, 0.6); } #free-clear-btn:active { transform: scale(0.98); } `); // 等待 DOM 就绪后注入按钮 let checkExist = setInterval(function () { if (document.body) { clearInterval(checkExist); let btn = document.createElement('button'); btn.id = 'free-clear-btn'; btn.textContent = '🧹 清空课程进度'; btn.addEventListener('click', function () { FREE.clearCourseProgress(); }); document.body.appendChild(btn); } }, 500); } }; setTimeout(() => { debugger if (typeof(zfk)=='undefined') { FREE.init(); } else { console.log('skip init'); } }, 3000); })();