// ==UserScript==
// @name 华医网全能助手(最新版)
// @namespace https://github.com/yourname/
// @version 2.2.0
// @description 自动静音播放、自动切换视频、屏蔽弹窗、智能考试、支持“选择答题模式”,修复课程完成页面跳转
// @match *://*.91huayi.com/course_ware/course_ware_polyv.aspx?*
// @match *://*.91huayi.com/course_ware/course_ware_cc.aspx?*
// @match *://*.91huayi.com/pages/exam.aspx?*
// @match *://*.91huayi.com/pages/exam_result.aspx?*
// @match *://*.91huayi.com/pages/course.aspx*
// @match *://*.91huayi.com/course_ware/course_list.aspx*
// @match *://*.91huayi.com/course_ware/course.aspx*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @grant GM_addStyle
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// ==================== 全局配置 ====================
const MODE_KEY = 'hy_mode_v2';
let currentMode = GM_getValue(MODE_KEY, 'video_exam'); // 'video_exam', 'video_only', 'exam_only'
// 考试相关存储
const STORAGE = {
test: 'hy_test',
result: 'hy_result',
testAnswer: 'hy_testAnswer',
rightAnswer: 'hy_rightAnswer',
allAnswer: 'hy_allAnswer',
thisTitle: 'hy_thisTitle',
};
const DELAY = {
submit: 6800,
retry: 3500,
examEnter: 7000,
random: 5000,
};
// ==================== 工具函数 ====================
function log(msg) { console.log(`[华医助手] ${msg}`); }
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
function randomDelay(min = 1000, max = 3000) { return Math.floor(Math.random() * (max - min + 1) + min); }
function getBaseUrl() { return window.location.origin; }
function getCwidFromUrl() { return new URLSearchParams(window.location.search).get('cwid'); }
// ==================== 调试窗口 ====================
function createDebugWindow() {
if (document.getElementById('hy-debug-box')) return;
const box = document.createElement('div');
box.id = 'hy-debug-box';
box.style.cssText = `
position: fixed; top: 12px; left: 12px; z-index: 9999999;
width: 420px; background: #1e293b; color: #e2e8f0;
padding: 16px; border-radius: 16px; font-size: 14px;
font-family: -apple-system, "PingFang SC", sans-serif;
box-shadow: 0 8px 30px rgba(0,0,0,0.5);
border: 1px solid #475569;
max-height: 80vh;
display: flex;
flex-direction: column;
`;
box.innerHTML = `
🩺 华医全能助手
✕
`;
document.body.appendChild(box);
document.getElementById('hy-close-debug').onclick = () => box.remove();
document.getElementById('hy-mode-select').onchange = function () {
currentMode = this.value;
GM_setValue(MODE_KEY, currentMode);
debugLog(`模式切换为: ${this.options[this.selectedIndex].text}`);
};
document.getElementById('hy-force-next').onclick = jumpToNextCourse;
document.getElementById('hy-force-exam').onclick = startExamQueueManually;
window.debugLog = function (msg) {
const logEl = document.getElementById('hy-log');
if (!logEl) return;
const time = new Date().toLocaleTimeString();
logEl.value = `[${time}] ${msg}\n` + logEl.value;
if (logEl.value.length > 2000) logEl.value = logEl.value.slice(0, 2000);
};
debugLog('🚀 脚本启动成功');
}
function debugLog(msg) { if (window.debugLog) window.debugLog(msg); else console.log(`[DEBUG] ${msg}`); }
// ==================== 弹窗拦截 ====================
function initPopupBlocker() {
setInterval(() => {
try {
if (window.player && typeof window.player.j2s_pauseVideo === 'function') {
window.player.j2s_pauseVideo = () => { debugLog('拦截 pauseVideo'); };
}
if (typeof window.initialSign !== 'undefined') {
window.initialSign = () => {
debugLog('自动签到');
if (typeof window.addPlaySign === 'function') window.addPlaySign();
window.isInitialSign = true;
};
}
} catch (e) {}
}, 500);
setInterval(() => {
try {
const ask = document.querySelector('.pv-ask-head');
if (ask) {
const skip = document.querySelector('.pv-ask-skip');
if (skip) { skip.click(); debugLog('已跳过课堂问答'); }
}
const sign = document.querySelector('.signBtn');
if (sign) { sign.click(); debugLog('已跳过签到'); }
const tip = document.querySelector('button[onclick="closeBangZhu()"], button[onclick="closeProcessbarTip()"]');
if (tip) { tip.click(); debugLog('已关闭温馨提示'); }
const know = document.querySelector('input.rig_btn[value="知道了"]');
if (know && document.getElementById('div_processbar_tip')?.style.display !== 'none') {
know.click();
}
const modeDialog = document.querySelector('.layui-layer-content');
if (modeDialog) {
const text = modeDialog.innerText;
if (text.includes('选择答题模式') || text.includes('考试模式') || text.includes('练习模式')) {
const btns = modeDialog.parentElement.querySelectorAll('.layui-layer-btn0, .layui-layer-btn1');
for (let btn of btns) {
if (btn.innerText.includes('正式考试') || btn.innerText.includes('考试')) {
btn.click();
debugLog('已选择“正式考试”模式');
break;
}
}
}
}
} catch (e) {}
try {
const video = document.querySelector('video');
if (video && video.paused && video.currentTime > 0 && video.currentTime < video.duration - 1) {
video.muted = true;
video.volume = 0;
video.play().catch(() => {});
debugLog('视频意外暂停,已恢复');
}
} catch (e) {}
}, 3000);
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
for (const node of m.addedNodes) {
if (node.nodeType !== 1) continue;
const text = node.innerText || '';
if (text.includes('选择答题模式')) {
const btns = node.querySelectorAll('.layui-layer-btn0, .layui-layer-btn1');
for (let btn of btns) {
if (btn.innerText.includes('正式考试') || btn.innerText.includes('考试')) {
btn.click();
debugLog('自动选择“正式考试”');
break;
}
}
}
if (node.classList?.contains('study_diaog')) {
node.remove();
debugLog('移除 study_diaog');
}
}
}
});
if (document.body) observer.observe(document.body, { childList: true, subtree: true });
}
// ==================== 视频播放 ====================
function setupVideo(video) {
if (video._hySetup) return;
video._hySetup = true;
video.muted = true;
video.volume = 0;
const playVideo = () => {
if (video.readyState >= 2) {
video.play().catch(() => {});
} else {
video.addEventListener('canplay', () => video.play(), { once: true });
}
};
setTimeout(playVideo, 1000);
setInterval(() => {
if (video.paused && video.currentTime > 0 && video.currentTime < video.duration - 1) {
video.muted = true;
video.volume = 0;
video.play().catch(() => {});
debugLog('守护恢复播放');
}
}, 5000);
video.addEventListener('ended', () => {
debugLog('视频播放结束');
setTimeout(() => {
// 检测是否已完成(通过状态文字)
const stateEl = document.querySelector('i[id="top_play"]')?.parentElement?.nextElementSibling?.nextElementSibling?.nextElementSibling;
let state = stateEl ? stateEl.innerText : '';
if (state === '已完成' || state === '待考试') {
if (currentMode === 'video_exam' && state === '待考试') {
const cwid = getCwidFromUrl();
if (cwid) {
const examUrl = `/pages/exam.aspx?cwid=${cwid}`;
debugLog('进入考试: ' + examUrl);
setTimeout(() => { window.location.href = examUrl; }, randomDelay(1000, 3000));
return;
}
}
// 否则跳转下一课(可能跳转到课程详情页)
jumpToNextCourse();
} else {
// 如果状态不是已完成,可能页面已跳转,尝试通用跳转
jumpToNextCourse();
}
}, 2000);
}, { once: true });
}
function observeVideos() {
const obs = new MutationObserver(() => {
document.querySelectorAll('video').forEach(v => setupVideo(v));
});
if (document.body) obs.observe(document.body, { childList: true, subtree: true });
document.querySelectorAll('video').forEach(v => setupVideo(v));
}
// ==================== 课程完成页面处理(新增) ====================
function handleCourseCompletePage() {
// 查找“立即学习”按钮
let learnBtn = document.querySelector('input[value="立即学习"]');
if (!learnBtn) learnBtn = document.querySelector('a[value="立即学习"]');
if (!learnBtn) {
// 通过文本查找
const btns = Array.from(document.querySelectorAll('button, input, a'));
for (let btn of btns) {
if (btn.innerText && btn.innerText.trim() === '立即学习') {
learnBtn = btn;
break;
}
}
}
if (learnBtn) {
const href = learnBtn.getAttribute('href');
if (href) {
debugLog('点击“立即学习”跳转到: ' + href);
window.location.href = href;
} else {
debugLog('点击“立即学习”按钮');
learnBtn.click();
}
return true;
}
return false;
}
// ==================== 跳课逻辑(增强) ====================
function jumpToNextCourse() {
debugLog('尝试跳转到下一课程');
// 1. 如果是课程完成页面,优先点击“立即学习”
if (handleCourseCompletePage()) {
return;
}
// 2. 如果是课程列表页
if (window.location.pathname.includes('/pages/course.aspx') || window.location.pathname.includes('/course_ware/course_list.aspx')) {
const items = document.querySelectorAll('.course, .lis-inside-content');
for (let item of items) {
if (item.innerText.includes('选修') || item.innerText.includes('互动')) continue;
const status = item.querySelector('span')?.innerText || item.querySelector('button')?.innerText || '';
if (status.includes('已完成') || status.includes('待考试')) continue;
const link = item.querySelector('a[href*="course_ware"]') || item.querySelector('h2[onclick]');
if (link) {
let url = link.href || link.getAttribute('onclick')?.match(/href='([^']+)'/)?.[1];
if (url) {
if (url.startsWith('http')) window.location.href = url;
else window.location.href = getBaseUrl() + url;
debugLog('跳转到: ' + url);
return;
}
}
}
debugLog('没有找到未学课程');
return;
}
// 3. 视频页面左侧列表(如果未跳转,尝试从目录找)
const items = document.querySelectorAll('li.lis-inside-content');
if (items.length === 0) {
debugLog('未找到目录列表');
return;
}
let currentIndex = -1;
for (let i = 0; i < items.length; i++) {
if (items[i].querySelector('#top_play')) {
currentIndex = i;
break;
}
}
let start = currentIndex + 1;
for (let i = start; i < items.length; i++) {
const li = items[i];
if (li.innerText.includes('选修') || li.innerText.includes('互动')) continue;
const btn = li.querySelector('button');
const text = btn ? btn.innerText.trim() : '';
if (text === '未学习' || text === '学习中' || text === '') {
const h2 = li.querySelector('h2');
if (h2) {
const onclick = h2.getAttribute('onclick');
if (onclick) {
const match = onclick.match(/window\.location\.href='([^']+)'/);
if (match) {
window.location.href = match[1];
debugLog('跳转下一节: ' + match[1]);
return;
}
}
const a = h2.querySelector('a');
if (a && a.href) {
window.location.href = a.href;
debugLog('跳转下一节: ' + a.href);
return;
}
}
}
}
debugLog('没有更多未学课程');
}
// ==================== 考试模块 ====================
function loadData(key) { return JSON.parse(GM_getValue(key, 'null')); }
function saveData(key, data) { GM_setValue(key, JSON.stringify(data)); }
function removeData(key) { GM_deleteValue(key); }
function normalize(txt) {
return txt ? txt.trim().replace(/^\d+、\s*/, '').replace(/[()()\s]/g, '') : '';
}
async function handleExamPage() {
debugLog('开始考试');
const questions = document.querySelectorAll('.tablestyle');
if (questions.length === 0) {
debugLog('未找到题目');
return;
}
const allAnswers = loadData(STORAGE.allAnswer) || {};
const title = document.title || '未知章节';
let rightAnswers = loadData(STORAGE.rightAnswer) || {};
if (Object.keys(rightAnswers).length === 0 && allAnswers[title]) {
rightAnswers = allAnswers[title];
}
let currentTest = loadData(STORAGE.test) || {};
let testAnswers = {};
for (let index = 0; index < questions.length; index++) {
const qEl = questions[index];
const qText = normalize(qEl.querySelector('.q_name')?.innerText || '');
if (!qText) continue;
const options = qEl.querySelectorAll('tbody label');
if (options.length === 0) continue;
if (rightAnswers[qText]) {
const answerText = rightAnswers[qText];
for (let opt of options) {
if (normalize(opt.innerText) === answerText) {
opt.click();
debugLog(`题目 "${qText}" 已选答案: ${answerText}`);
break;
}
}
continue;
}
let selected = false;
const wrongs = loadData(STORAGE.result) || {};
const wrongList = wrongs[qText] || [];
for (let opt of options) {
const content = normalize(opt.innerText);
if (!wrongList.includes(content)) {
opt.click();
testAnswers[qText] = content;
if (!currentTest[qText]) currentTest[qText] = [];
currentTest[qText].push(content);
selected = true;
debugLog(`试错选择: "${qText}" -> "${content}"`);
break;
}
}
if (!selected && options.length > 0) {
options[0].click();
testAnswers[qText] = normalize(options[0].innerText);
debugLog(`所有选项试过,选第一个: "${qText}"`);
}
await sleep(randomDelay(500, 1500));
}
saveData(STORAGE.test, currentTest);
saveData(STORAGE.testAnswer, testAnswers);
setTimeout(() => {
const btn = document.getElementById('btn_submit');
if (btn) {
btn.click();
debugLog('已交卷');
} else {
debugLog('找不到提交按钮');
}
}, DELAY.submit + randomDelay(0, DELAY.random));
}
async function handleResultPage() {
debugLog('处理考试结果');
const resultText = document.querySelector('.tips_text')?.innerText || '';
const isPassed = resultText.includes('考试通过') || resultText.includes('完成项目学习');
const wrongs = loadData(STORAGE.result) || {};
const testAnswers = loadData(STORAGE.testAnswer) || {};
const rightAnswers = loadData(STORAGE.rightAnswer) || {};
const allAnswers = loadData(STORAGE.allAnswer) || {};
const title = document.title || '未知章节';
if (isPassed) {
debugLog('考试通过,记录正确答案');
for (let q in testAnswers) {
if (!wrongs[q]) {
rightAnswers[q] = testAnswers[q];
}
}
if (!allAnswers[title]) allAnswers[title] = {};
Object.assign(allAnswers[title], rightAnswers);
saveData(STORAGE.allAnswer, allAnswers);
saveData(STORAGE.rightAnswer, rightAnswers);
removeData(STORAGE.test);
removeData(STORAGE.testAnswer);
removeData(STORAGE.result);
const cwid = getCwidFromUrl();
if (cwid) {
const videoUrl = `/course_ware/course_ware_polyv.aspx?cwid=${cwid}`;
debugLog('跳回视频: ' + videoUrl);
setTimeout(() => { window.location.href = videoUrl; }, 2000);
} else {
debugLog('未找到cwid,跳转到课程列表');
setTimeout(() => { window.location.href = '/pages/course.aspx'; }, 2000);
}
} else {
debugLog('考试未通过,记录错题');
const items = document.querySelectorAll('.state_cour_lis');
let newWrongs = {};
for (let item of items) {
const qTitle = item.querySelector('.state_lis_text:first-of-type')?.title;
if (!qTitle) continue;
const ansText = item.querySelectorAll('.state_lis_text')[1]?.innerText?.replace(/【您的答案:|】/g, '') || '';
const isCorrect = item.querySelector('.state_error')?.src?.includes('bar_img.png') || false;
const qKey = normalize(qTitle);
if (!isCorrect && qKey) {
if (!newWrongs[qKey]) newWrongs[qKey] = [];
newWrongs[qKey].push(normalize(ansText));
}
}
for (let q in newWrongs) {
if (!wrongs[q]) wrongs[q] = [];
for (let ans of newWrongs[q]) {
if (!wrongs[q].includes(ans)) wrongs[q].push(ans);
}
}
saveData(STORAGE.result, wrongs);
const retryBtn = document.querySelector('input[value="重新考试"]');
if (retryBtn) {
setTimeout(() => retryBtn.click(), DELAY.retry + randomDelay(0, DELAY.random));
debugLog('点击重新考试');
} else {
debugLog('未找到重新考试按钮');
}
}
}
function startExamQueueManually() {
debugLog('手动触发考试扫描');
if (window.location.pathname.includes('/pages/course.aspx')) {
const items = document.querySelectorAll('.course');
for (let item of items) {
if (item.innerText.includes('选修') || item.innerText.includes('互动')) continue;
const status = item.querySelector('span')?.innerText || '';
if (status.includes('待考试')) {
const link = item.querySelector('a[href*="exam"]');
if (link) {
window.location.href = link.href;
debugLog('进入待考课程');
return;
}
}
}
debugLog('没有待考课程');
} else {
const cwid = getCwidFromUrl();
if (cwid) {
window.location.href = `/pages/exam.aspx?cwid=${cwid}`;
debugLog('直接进入考试');
}
}
}
// ==================== 主路由 ====================
function main() {
createDebugWindow();
initPopupBlocker();
const path = window.location.pathname;
// 处理课程完成页面(包含“本课件已学习完毕”或“立即学习”按钮)
if (path.includes('/pages/course.aspx') || path.includes('/course_ware/course.aspx') || path.includes('/course_ware/course_list.aspx')) {
debugLog('课程页面');
// 优先处理完成页面跳转
if (handleCourseCompletePage()) {
return;
}
// 否则走原有逻辑
if (currentMode !== 'exam_only') {
setTimeout(jumpToNextCourse, 2000);
} else {
debugLog('仅考试模式,不自动跳视频');
}
if (currentMode === 'video_exam' || currentMode === 'exam_only') {
setTimeout(startExamQueueManually, 3000);
}
return;
}
if (path.includes('course_ware_polyv.aspx') || path.includes('course_ware_cc.aspx')) {
debugLog('视频页面');
if (currentMode === 'exam_only') {
debugLog('仅考试模式,不播放视频');
return;
}
observeVideos();
const examBtn = document.getElementById('jrks');
if (examBtn) {
const check = setInterval(() => {
if (examBtn.getAttribute('disabled') === null) {
clearInterval(check);
debugLog('考试按钮已激活');
if (currentMode === 'video_exam') {
const cwid = getCwidFromUrl();
if (cwid) {
setTimeout(() => {
window.location.href = `/pages/exam.aspx?cwid=${cwid}`;
}, DELAY.examEnter + randomDelay(0, DELAY.random));
}
} else {
setTimeout(jumpToNextCourse, 3000);
}
}
}, 2000);
}
setTimeout(() => {
const qBtn = document.querySelector('.pv-quality-btn');
if (qBtn) { qBtn.click(); setTimeout(() => { const f = Array.from(document.querySelectorAll('.pv-quality-select div')).find(d => d.innerText === '流畅'); if (f) f.click(); }, 500); }
}, 3000);
return;
}
if (path.includes('/exam.aspx')) {
debugLog('考试页面');
if (currentMode !== 'video_only') {
handleExamPage();
} else {
debugLog('仅视频模式,跳过考试');
}
return;
}
if (path.includes('/exam_result.aspx')) {
debugLog('考试结果页面');
if (currentMode !== 'video_only') {
handleResultPage();
} else {
debugLog('仅视频模式,跳过考试结果处理');
}
return;
}
debugLog('未知页面,不执行任务');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', main);
} else {
main();
}
})();