Advance when learning is complete and the platform exam is passed

This commit is contained in:
2026-10-06 02:38:21 +08:00
parent 606d264917
commit d80c490f48
5 changed files with 63 additions and 38 deletions
+17 -16
View File
@@ -1,7 +1,7 @@
// ==UserScript==
// @name 学员中心 · 2倍速学习助手
// @namespace local.icampus.study
// @version 1.3.0
// @version 1.3.1
// @description 支持切换标签页继续2倍速播放与保存进度,自动测验及错题重试。
// @match https://icampus.yunyinsh.com/course/*
// @run-at document-start
@@ -28,14 +28,19 @@
function bodyJSON(body) {
try { return typeof body === 'string' && body.length < 65536 ? JSON.parse(body) : null; } catch { return null; }
}
function examPerfect(c) { return !!c && Number(c.examScorePercent)===100 && (c.examPassed===true || c.examPassed==='true'); }
function courseDone(c) { return !!c && (c.status === 'COMPLETED' || !!c.completedAt) && (!c.examRequired || examPerfect(c)); }
function examPassed(c) { return !!c && (c.examPassed===true || c.examPassed==='true'); }
function courseDone(c) {
if(!c) return false;
const hasProgress=c.progressPercent!==null && c.progressPercent!==undefined && c.progressPercent!=='';
const learningDone=hasProgress ? Number(c.progressPercent)>=100 : c.status==='COMPLETED' || !!c.completedAt;
return learningDone && (!c.examRequired || examPassed(c));
}
function videoPlan(detail) {
if (!detail?.course || !Array.isArray(detail.resources)) return {kind: 'wait'};
const resources = [...detail.resources].sort((a, b) => a.sortNo - b.sortNo);
const next = resources.find(r => r.completed !== true);
if (next) return {kind: next.type === 'VIDEO' ? 'video' : 'manual', resource: next};
if (detail.course.examRequired && !examPerfect(detail.course)) return {kind: 'quiz'};
if (detail.course.examRequired && !examPassed(detail.course)) return {kind: 'quiz'};
return {kind: 'finish'};
}
function resolveCards(cards, courses) {
@@ -92,7 +97,7 @@
return m ? {kind:'card', paperId:m[1]} : null;
} catch { return null; }
}
const core = {normalize, route, endpoint, bodyJSON, courseDone, examPerfect, videoPlan, resolveCards, leaseAvailable,
const core = {normalize, route, endpoint, bodyJSON, courseDone, examPassed, videoPlan, resolveCards, leaseAvailable,
questionKey, selectionKey, chooseAnswer, learnCard, quizEndpoint};
if (typeof module === 'object' && module.exports && typeof window === 'undefined') { module.exports = core; return; }
if (window.top !== window.self || document.getElementById('icampus-study-helper')) return;
@@ -406,13 +411,9 @@
const percent = result.querySelector('.quiz-result__score strong')?.textContent.trim();
const score=percent && /^\d+(\.\d+)?%$/.test(percent) ? Number(percent.replace('%','')) : course?.examScorePercent;
if(course && score!==undefined) course.examScorePercent=score;
if(Number(score)===100) {
log('测验已达到100%,允许继续。');
if(quizOnly) return park('本门测验已达到100%,保持已启动,等待后续操作。',{passive:true});
quiz=null; return;
}
// A lower platform pass mark does not satisfy the user's 100% gate.
if(!quizButton(root,'重新测验')) return park(`当前正确率${score ?? '未知'}%,等待重考入口;达到100%前不切换视频或课程。`);
log(`平台已确认测验合格(${score ?? '未显示'}%),继续检查学习进度。`);
if(quizOnly) return park('本门测验已合格,保持已启动,等待后续操作。',{passive:true});
quiz=null; return;
}
if (quiz.phase === 'opening' || quiz.phase === 'retrying') return quizWait('正在打开测验…');
const exam = exams.get(id);
@@ -581,7 +582,7 @@
return;
}
const plan = videoPlan(detail);
if(detail.course.examRequired && !examPerfect(detail.course)) {
if(detail.course.examRequired && !examPassed(detail.course)) {
const gatedVideo=active?.video || currentVideo();
if(gatedVideo && !gatedVideo.paused) gatedVideo.pause();
active=null;
@@ -730,14 +731,14 @@
host.style.cssText = 'position:fixed;left:16px;bottom:16px;z-index:2147483646';
panel = host.attachShadow({mode: 'open'});
panel.innerHTML = `<style>:host{font:14px/1.6 system-ui;color:#eaf0fa}section{width:290px;background:#17243a;border:1px solid #6883a6;border-radius:12px;padding:15px;box-shadow:0 5px 24px #0005}strong{font-size:16px}p{margin:9px 0;white-space:pre-line}small{color:#b7c7df}button,select{font:inherit;border-radius:6px;border:1px solid #7186a5;padding:5px 9px;margin:3px 5px 3px 0}button{cursor:pointer;background:#dbeaff;color:#123}button:disabled{opacity:.5;cursor:default}select{width:100%;background:#fff;color:#123}[data-status]{min-height:45px}details{margin-top:8px}pre{white-space:pre-wrap;max-height:160px;overflow:auto;font-size:12px}</style>
<section><strong>2倍速学习助手 · 1.3.0</strong><p data-status></p><small data-progress></small>
<section><strong>2倍速学习助手 · 1.3.1</strong><p data-status></p><small data-progress></small>
<select aria-label="播放范围"><option value="all">按列表顺序继续课程</option><option value="current">仅当前课程</option></select>
<div><button data-start>开始</button><button data-stop>人工取消</button></div>
<label><input type="checkbox" data-quiz checked> 自动测验,达到100%才继续</label>
<label><input type="checkbox" data-quiz checked> 自动测验,按平台合格线通过</label>
<div><label><input type="checkbox" data-background> 切标签页继续播放</label></div>
<small data-background-state></small>
<div><button data-quiz-only>只做本门测验</button></div>
<small>启动后持续运行与等待;只有人工取消才关闭。测验100%后继续视频。</small>
<small>学习进度100%且测验合格后换课。持续运行,人工取消才关闭。</small>
<details><summary>运行记录</summary><pre></pre><button data-copy>更新记录</button></details></section>`;
mode = panel.querySelector('select');
autoQuiz = panel.querySelector('[data-quiz]');