courseNavigation

岐黄天使刷课助手的课程导航功能模块,负责课程的识别、切换和状态管理。

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/537075/1597552/courseNavigation.js

  1. // ==UserScript==
  2. // @name 岐黄天使刷课助手 - 课程导航模块
  3. // @namespace http://tampermonkey.net/qhtx-modules
  4. // @version 1.3.1
  5. // @description 岐黄天使刷课助手的课程导航功能模块,负责课程的识别、切换和状态管理。
  6. // @author AI助手
  7. // ==/UserScript==
  8.  
  9. // 课程导航模块
  10. (function() {
  11. 'use strict';
  12.  
  13. // 收集课程链接,用于上一课/下一课导航
  14. window.collectCourseLinks = function(doc) {
  15. try {
  16. // 收集所有课程链接(包括已完成和未完成的)
  17. const allCourseLinks = doc.querySelectorAll('.append-plugin-tip > a, .content-unstart a, .content-learning a, .content-finished a');
  18.  
  19. if (allCourseLinks.length > 0) {
  20. console.log('找到课程链接数量:', allCourseLinks.length);
  21.  
  22. window.qh.courseList = Array.from(allCourseLinks).map((link, index) => {
  23. return {
  24. index: index,
  25. title: link.textContent.trim() || `课程 ${index + 1}`,
  26. element: link,
  27. status: link.closest('.content-finished') ? '已完成' : '未完成'
  28. };
  29. });
  30.  
  31. // 找到当前正在播放的课程
  32. const activeLink = doc.querySelector('.active a');
  33. if (activeLink) {
  34. const activeIndex = Array.from(allCourseLinks).findIndex(link => link.href === activeLink.href);
  35. if (activeIndex !== -1) {
  36. window.qh.currentCourseIndex = activeIndex;
  37. console.log('当前课程索引:', window.qh.currentCourseIndex, '课程标题:', window.qh.courseList[window.qh.currentCourseIndex].title);
  38. }
  39. } else {
  40. // 如果找不到活动链接,尝试通过其他方式确定当前课程
  41. const activeItem = doc.querySelector('.active') || doc.querySelector('.content-learning');
  42. if (activeItem) {
  43. // 尝试找到最接近的课程链接
  44. const nearestLink = activeItem.querySelector('a');
  45. if (nearestLink) {
  46. const nearestIndex = Array.from(allCourseLinks).findIndex(link =>
  47. link.textContent.trim() === nearestLink.textContent.trim());
  48. if (nearestIndex !== -1) {
  49. window.qh.currentCourseIndex = nearestIndex;
  50. console.log('通过活动项找到当前课程索引:', window.qh.currentCourseIndex);
  51. }
  52. }
  53. }
  54. }
  55.  
  56. // 只有在面板已创建的情况下才更新导航按钮
  57. if (window.qh.panelCreated && document.getElementById('qh-assistant-panel')) {
  58. updateNavButtons();
  59. }
  60. return;
  61. } else {
  62. console.log('未找到标准课程链接,尝试查找其他课程元素');
  63.  
  64. // 尝试查找新的课程结构 (.new_bg 元素)
  65. // 处理多层嵌套目录结构
  66. const courseSections = doc.querySelectorAll('.muli1, .xulun');
  67. courseSections.forEach(section => {
  68. const titles = section.querySelectorAll('.title');
  69. titles.forEach(title => {
  70. const chapterId = title.id || '';
  71. const chapterNum = title.querySelector('b')?.textContent.trim() || '';
  72. const chapterName = title.querySelector('span')?.textContent.trim() || '';
  73.  
  74. // 获取子课程项
  75. const courseItems = title.nextElementSibling?.querySelectorAll('.new_bg') || [];
  76. courseItems.forEach(item => {
  77. const courseId = item.id;
  78. const onclickFn = item.getAttribute('onclick') || '';
  79. const videoIdMatch = onclickFn.match(/getvideoUrl\((\d+),/);
  80. const videoId = videoIdMatch ? videoIdMatch[1] : '';
  81.  
  82. const titleElement = item.querySelector('.new_biaoti');
  83. let courseTitle = titleElement?.textContent.replace(/^视频\s*/, '') || '';
  84.  
  85. // 添加data属性
  86. item.dataset.courseId = courseId;
  87. item.dataset.videoId = videoId;
  88. item.dataset.chapterId = chapterId;
  89.  
  90. window.qh.courseList.push({
  91. id: courseId,
  92. videoId: videoId,
  93. chapterId: chapterId,
  94. title: `${chapterNum} ${chapterName} - ${courseTitle}`,
  95. element: item,
  96. status: item.querySelector('.text-primary') ? '已完成' : '进行中'
  97. });
  98. });
  99. });
  100. });
  101.  
  102. if (window.qh.courseList.length > 0) {
  103. console.log('解析到嵌套课程结构数量:', window.qh.courseList.length);
  104.  
  105. // 使用getCourseList函数获取按顺序排列的课程列表
  106. window.qh.courseList = getCourseList(doc);
  107.  
  108. // 增强活动项检测逻辑
  109. const activeItem = doc.querySelector('.new_bg.active, .new_bg[style*="background"], .studystate.text-success');
  110. if (activeItem) {
  111. const newBgElements = doc.querySelectorAll('.new_bg');
  112. const activeIndex = Array.from(newBgElements).indexOf(activeItem);
  113. if (activeIndex !== -1) {
  114. window.qh.currentCourseIndex = activeIndex;
  115. console.log('从新课程结构中找到当前课程索引:', window.qh.currentCourseIndex);
  116. }
  117. }
  118.  
  119. // 只有在面板已创建的情况下才更新导航按钮
  120. if (window.qh.panelCreated && document.getElementById('qh-assistant-panel')) {
  121. updateNavButtons();
  122. }
  123. return;
  124. }
  125.  
  126. // 尝试从其他元素收集课程信息
  127. const courseItems = doc.querySelectorAll('.course-item, .video-item, li[data-id], .kecheng-item');
  128. if (courseItems.length > 0) {
  129. console.log('找到备选课程项数量:', courseItems.length);
  130.  
  131. window.qh.courseList = Array.from(courseItems).map((item, index) => {
  132. const link = item.querySelector('a') || item;
  133. let title = '';
  134.  
  135. // 尝试从不同元素获取标题
  136. const titleElement = item.querySelector('.title, .name, .course-name, .video-title');
  137. if (titleElement) {
  138. title = titleElement.textContent.trim();
  139. } else {
  140. title = item.textContent.trim() || `课程 ${index + 1}`;
  141. }
  142.  
  143. // 确定状态
  144. const status = item.classList.contains('finished') ||
  145. item.classList.contains('complete') ||
  146. item.querySelector('.complete, .finished, .done') ?
  147. '已完成' : '未完成';
  148.  
  149. return {
  150. index: index,
  151. title: title,
  152. element: link,
  153. status: status
  154. };
  155. });
  156.  
  157. // 尝试找到当前活动项
  158. const activeItem = doc.querySelector('.active, .current, .playing, .selected');
  159. if (activeItem) {
  160. const activeIndex = Array.from(courseItems).indexOf(activeItem);
  161. if (activeIndex !== -1) {
  162. window.qh.currentCourseIndex = activeIndex;
  163. console.log('从备选项中找到当前课程索引:', window.qh.currentCourseIndex);
  164. }
  165. }
  166.  
  167. // 只有在面板已创建的情况下才更新导航按钮
  168. if (window.qh.panelCreated && document.getElementById('qh-assistant-panel')) {
  169. updateNavButtons();
  170. }
  171. }
  172. }
  173. } catch (e) {
  174. console.error('收集课程链接时发生错误:', e);
  175. }
  176. };
  177.  
  178. // 从课程列表中收集课程
  179. window.collectCoursesFromList = function(doc) {
  180. try {
  181. const coursesInList = doc.querySelectorAll('.mycourse-row');
  182.  
  183. if (coursesInList.length > 0) {
  184. window.qh.courseList = Array.from(coursesInList).map((row, index) => {
  185. const link = row.querySelector('a');
  186. const title = row.innerText.split('\t')[0].split('\n\n')[1]?.replace("\n", "") || `课程 ${index + 1}`;
  187. const status = row.innerText.includes('未完成') || row.innerText.includes('未开始') ? '未完成' : '已完成';
  188.  
  189. return {
  190. index: index,
  191. title: title,
  192. element: link,
  193. status: status
  194. };
  195. });
  196.  
  197. // 只有在面板已创建的情况下才更新导航按钮
  198. if (window.qh.panelCreated && document.getElementById('qh-assistant-panel')) {
  199. updateNavButtons();
  200. }
  201. }
  202. } catch (e) {
  203. console.error('从课程列表收集课程时发生错误:', e);
  204. }
  205. };
  206.  
  207. // 更新导航按钮状态
  208. window.updateNavButtons = function() {
  209. // 延迟执行,确保面板已创建
  210. setTimeout(() => {
  211. const prevBtn = document.getElementById('qh-prev-btn');
  212. const nextBtn = document.getElementById('qh-next-btn');
  213.  
  214. if (!prevBtn || !nextBtn) {
  215. console.debug('导航按钮元素未找到,可能面板未完全创建');
  216. return;
  217. }
  218.  
  219. // 确保课程列表和索引已初始化
  220. if (!window.qh.courseList) {
  221. window.qh.courseList = [];
  222. }
  223.  
  224. if (typeof window.qh.currentCourseIndex !== 'number') {
  225. window.qh.currentCourseIndex = 0;
  226. }
  227.  
  228. // 检查课程列表是否有内容
  229. if (window.qh.courseList.length === 0) {
  230. prevBtn.disabled = true;
  231. nextBtn.disabled = true;
  232. prevBtn.textContent = '上一课';
  233. nextBtn.textContent = '下一课';
  234. console.log('课程列表为空,禁用导航按钮');
  235.  
  236. // 尝试重新收集课程列表
  237. if (typeof collectCourseLinks === 'function') {
  238. console.log('尝试重新收集课程列表...');
  239. collectCourseLinks(document);
  240. }
  241. }
  242.  
  243. // 更新按钮状态
  244. const canGoPrev = window.qh.currentCourseIndex > 0;
  245. const canGoNext = window.qh.currentCourseIndex < window.qh.courseList.length - 1;
  246.  
  247. prevBtn.disabled = !canGoPrev;
  248. nextBtn.disabled = !canGoNext;
  249.  
  250. console.log('更新导航按钮状态:',
  251. '上一课:', canGoPrev ? '启用' : '禁用',
  252. '下一课:', canGoNext ? '启用' : '启用',
  253. '当前索引:', window.qh.currentCourseIndex,
  254. '课程总数:', window.qh.courseList.length);
  255.  
  256. // 更新按钮文本,显示上一课/下一课的标题
  257. if (window.qh.currentCourseIndex > 0 && window.qh.courseList[window.qh.currentCourseIndex - 1]) {
  258. const prevTitle = window.qh.courseList[window.qh.currentCourseIndex - 1].title;
  259. prevBtn.title = prevTitle;
  260. // 如果标题太长,截断显示
  261. prevBtn.textContent = '上一课: ' + (prevTitle.length > 10 ? prevTitle.substring(0, 10) + '...' : prevTitle);
  262. } else {
  263. prevBtn.textContent = '上一课';
  264. }
  265.  
  266. if (window.qh.currentCourseIndex < window.qh.courseList.length - 1 && window.qh.courseList[window.qh.currentCourseIndex + 1]) {
  267. const nextTitle = window.qh.courseList[window.qh.currentCourseIndex + 1].title;
  268. nextBtn.title = nextTitle;
  269. // 如果标题太长,截断显示
  270. nextBtn.textContent = '下一课: ' + (nextTitle.length > 10 ? nextTitle.substring(0, 10) + '...' : nextTitle);
  271. } else {
  272. nextBtn.textContent = '下一课';
  273. }
  274.  
  275. // 重新绑定按钮点击事件,确保它们能正常工作
  276. // 先移除旧的事件监听器,避免重复绑定
  277. const oldPrevHandler = prevBtn.qhPrevHandler;
  278. const oldNextHandler = nextBtn.qhNextHandler;
  279. if (oldPrevHandler) prevBtn.removeEventListener('click', oldPrevHandler);
  280. if (oldNextHandler) nextBtn.removeEventListener('click', oldNextHandler);
  281.  
  282. // 添加新的事件监听器
  283. const newPrevHandler = function() {
  284. console.log('上一课按钮点击,当前索引:', window.qh.currentCourseIndex);
  285. if (window.qh.currentCourseIndex > 0) {
  286. window.qh.currentCourseIndex--;
  287. console.log('新课程索引:', window.qh.currentCourseIndex, '课程标题:', window.qh.courseList[window.qh.currentCourseIndex]?.title);
  288. navigateToCourse(window.qh.currentCourseIndex);
  289. updateNavButtons();
  290. }
  291. };
  292.  
  293. const newNextHandler = function() {
  294. console.log('下一课按钮点击,当前索引:', window.qh.currentCourseIndex);
  295. if (window.qh.currentCourseIndex < window.qh.courseList.length - 1) {
  296. window.qh.currentCourseIndex++;
  297. console.log('新课程索引:', window.qh.currentCourseIndex, '课程标题:', window.qh.courseList[window.qh.currentCourseIndex]?.title);
  298. navigateToCourse(window.qh.currentCourseIndex);
  299. updateNavButtons();
  300. }
  301. };
  302.  
  303. prevBtn.addEventListener('click', newPrevHandler);
  304. nextBtn.addEventListener('click', newNextHandler);
  305.  
  306. // 保存处理器引用以便后续移除
  307. prevBtn.qhPrevHandler = newPrevHandler;
  308. nextBtn.qhNextHandler = newNextHandler;
  309.  
  310. }, 100); // 延迟100ms执行
  311. };
  312.  
  313. // 获取课程列表,按照DOM顺序排列
  314. function getCourseList(doc) {
  315. try {
  316. // 尝试查找所有可能的课程元素
  317. const courseElements = doc.querySelectorAll('.new_bg[onclick*="getvideoUrl"], .new_bg[onclick*="setti"], .kecheng-item[onclick], li[onclick*="getvideoUrl"], li[onclick*="setti"]');
  318.  
  319. if (courseElements.length === 0) {
  320. console.log('未找到课程元素,尝试查找其他类型的课程元素');
  321. console.log('当前DOM结构:', doc.documentElement.outerHTML.substring(0, 500) + '...');
  322. console.log('尝试查找的课程元素选择器:', '.new_bg[onclick*="getvideoUrl"], .new_bg[onclick*="setti"], .kecheng-item[onclick], li[onclick*="getvideoUrl"], li[onclick*="setti"]');
  323. return [];
  324. }
  325.  
  326. console.log(`找到 ${courseElements.length} 个可能的课程元素`);
  327. if (courseElements.length > 0) {
  328. console.log('第一个课程元素信息:', {
  329. id: courseElements[0].id,
  330. onclick: courseElements[0].getAttribute('onclick'),
  331. classList: Array.from(courseElements[0].classList),
  332. outerHTML: courseElements[0].outerHTML.substring(0, 200) + '...'
  333. });
  334. }
  335. const courses = [];
  336.  
  337. // 创建一个映射来存储课程ID和它们的DOM元素
  338. const courseMap = new Map();
  339.  
  340. courseElements.forEach(item => {
  341. const id = item.id || '';
  342. const onclickAttr = item.getAttribute('onclick') || '';
  343.  
  344. // 提取标题
  345. const titleElement = item.querySelector('.new_biaoti, .title, .name, .course-name');
  346. let title = titleElement ? titleElement.textContent.trim() : '';
  347. // 如果没有找到标题元素,尝试从元素本身获取文本
  348. if (!title && item.textContent) {
  349. title = item.textContent.trim();
  350. }
  351. // 移除"视频"等前缀
  352. title = title.replace(/^(视频|音频|文档)\s*/, '');
  353.  
  354. // 确定状态
  355. const statusElement = item.querySelector('.studystate, .status, .complete, .finished');
  356. const status = (statusElement && (statusElement.textContent.includes('已学完') || statusElement.textContent.includes('已完成')))
  357. || item.classList.contains('finished')
  358. || item.classList.contains('complete')
  359. ? '已完成' : '未完成';
  360.  
  361. // 尝试从onclick属性中提取课程ID和标题
  362. let courseId = id;
  363. let matchFound = false;
  364.  
  365. // 尝试匹配getvideoUrl模式
  366. const getvideoUrlMatch = onclickAttr.match(/getvideoUrl\(([^,]+),\s*['"]([^'"]+)['"]\)/);
  367. if (getvideoUrlMatch) {
  368. courseId = getvideoUrlMatch[1];
  369. const matchTitle = getvideoUrlMatch[2];
  370. if (!title && matchTitle) {
  371. title = matchTitle;
  372. }
  373. matchFound = true;
  374. }
  375.  
  376. // 尝试匹配setti模式
  377. const settiMatch = onclickAttr.match(/setti\(([^)]+)\)/);
  378. if (!matchFound && settiMatch) {
  379. courseId = settiMatch[1];
  380. matchFound = true;
  381. }
  382.  
  383. // 如果找到了匹配,或者onclick属性包含关键字
  384. if (matchFound || onclickAttr.includes('getvideoUrl') || onclickAttr.includes('setti')) {
  385. courseMap.set(courseId, {
  386. id: courseId,
  387. title: title || `课程 ${courseId}`,
  388. element: item,
  389. status: status,
  390. onclickFn: onclickAttr
  391. });
  392. }
  393. });
  394.  
  395. // 获取所有可能的章节容器
  396. const chapterContainers = doc.querySelectorAll('.muli1, .chapter, .section, .module');
  397.  
  398. // 如果找到章节容器,按照DOM顺序遍历章节和课程
  399. if (chapterContainers.length > 0) {
  400. console.log(`找到 ${chapterContainers.length} 个章节容器`);
  401. let index = 0;
  402. chapterContainers.forEach(chapter => {
  403. // 查找章节内的所有课程元素
  404. const courseElementsInChapter = chapter.querySelectorAll('.new_bg[onclick], .kecheng-item[onclick], li[onclick]');
  405. courseElementsInChapter.forEach(element => {
  406. const id = element.id || '';
  407. const onclickAttr = element.getAttribute('onclick') || '';
  408.  
  409. // 尝试从onclick属性中提取课程ID
  410. let courseId = id;
  411.  
  412. // 尝试匹配getvideoUrl模式
  413. const getvideoUrlMatch = onclickAttr.match(/getvideoUrl\(([^,]+),/);
  414. if (getvideoUrlMatch) {
  415. courseId = getvideoUrlMatch[1];
  416. }
  417.  
  418. // 尝试匹配setti模式
  419. const settiMatch = onclickAttr.match(/setti\(([^)]+)\)/);
  420. if (settiMatch) {
  421. courseId = settiMatch[1];
  422. }
  423.  
  424. if (courseMap.has(courseId)) {
  425. const course = courseMap.get(courseId);
  426. course.index = index++;
  427. courses.push(course);
  428. } else if (courseMap.has(id)) {
  429. const course = courseMap.get(id);
  430. course.index = index++;
  431. courses.push(course);
  432. }
  433. });
  434. });
  435. }
  436.  
  437. // 如果通过章节容器没有找到课程,或者找到的课程数量少于总课程数量的一半,
  438. // 则直接按照DOM顺序添加所有课程
  439. if (courses.length === 0 || courses.length < courseMap.size / 2) {
  440. console.log(`通过章节容器只找到 ${courses.length} 个课程,直接按DOM顺序添加所有课程`);
  441. courses.length = 0; // 清空数组
  442. let index = 0;
  443. courseElements.forEach(element => {
  444. const id = element.id || '';
  445. const onclickAttr = element.getAttribute('onclick') || '';
  446.  
  447. // 尝试从onclick属性中提取课程ID
  448. let courseId = id;
  449.  
  450. // 尝试匹配getvideoUrl模式
  451. const getvideoUrlMatch = onclickAttr.match(/getvideoUrl\(([^,]+),/);
  452. if (getvideoUrlMatch) {
  453. courseId = getvideoUrlMatch[1];
  454. }
  455.  
  456. // 尝试匹配setti模式
  457. const settiMatch = onclickAttr.match(/setti\(([^)]+)\)/);
  458. if (settiMatch) {
  459. courseId = settiMatch[1];
  460. }
  461.  
  462. if (courseMap.has(courseId)) {
  463. const course = courseMap.get(courseId);
  464. course.index = index++;
  465. courses.push(course);
  466. } else if (courseMap.has(id)) {
  467. const course = courseMap.get(id);
  468. course.index = index++;
  469. courses.push(course);
  470. }
  471. });
  472. }
  473.  
  474. console.log(`最终找到 ${courses.length} 个课程,按顺序排列`);
  475. if (courses.length > 0) {
  476. console.log('第一个课程详细信息:', {
  477. id: courses[0].id,
  478. title: courses[0].title,
  479. status: courses[0].status,
  480. element: courses[0].element ? courses[0].element.outerHTML.substring(0, 200) + '...' : null
  481. });
  482. }
  483. return courses;
  484. } catch (error) {
  485. console.error('获取课程列表出错:', error);
  486.  
  487. // 如果出错,回退到简单的映射方法
  488. const courseElements = doc.querySelectorAll('.new_bg[onclick], .kecheng-item[onclick], li[onclick*="getvideoUrl"], li[onclick*="setti"]');
  489. return Array.from(courseElements).map((item, index) => {
  490. // 从onclick属性中提取课程ID和标题
  491. const onclickAttr = item.getAttribute('onclick') || '';
  492. const courseId = item.id || '';
  493.  
  494. // 提取标题
  495. const titleElement = item.querySelector('.new_biaoti, .title, .name, .course-name');
  496. let title = titleElement ? titleElement.textContent.trim() : '';
  497. // 如果没有找到标题元素,尝试从元素本身获取文本
  498. if (!title && item.textContent) {
  499. title = item.textContent.trim();
  500. }
  501. // 移除"视频"等前缀
  502. title = title.replace(/^(视频|音频|文档)\s*/, '');
  503.  
  504. // 确定状态
  505. const statusElement = item.querySelector('.studystate, .status, .complete, .finished');
  506. const status = (statusElement && (statusElement.textContent.includes('已学完') || statusElement.textContent.includes('已完成')))
  507. || item.classList.contains('finished')
  508. || item.classList.contains('complete')
  509. ? '已完成' : '未完成';
  510.  
  511. return {
  512. index: index,
  513. id: courseId,
  514. title: title || `课程 ${index + 1}`,
  515. element: item,
  516. status: status,
  517. onclickFn: onclickAttr
  518. };
  519. });
  520. }
  521. }
  522.  
  523. // 导航到指定索引的课程
  524. function navigateToCourse(index) {
  525. console.log('正在切换课程,目标索引:', index);
  526. try {
  527. if (index < 0 || index >= window.qh.courseList.length) {
  528. console.error('无效的课程索引:', index);
  529. return;
  530. }
  531.  
  532. const targetCourse = window.qh.courseList[index];
  533. if (targetCourse?.element) {
  534. console.log('正在跳转至课程:', targetCourse.title);
  535. targetCourse.element.click();
  536. // 添加防抖处理防止重复点击
  537. setTimeout(() => updateNavButtons(), 500);
  538. }
  539. } catch (e) {
  540. console.error('课程切换失败:', e);
  541. }
  542. }
  543.  
  544. // 导航到上一课
  545. window.navigateToPrevCourse = function() {
  546. // 动态更新课程列表
  547. const mainFrame = document.getElementById('taocan_main_frame');
  548. const targetDoc = mainFrame ? mainFrame.contentDocument : document;
  549. collectCourseLinks(targetDoc);
  550.  
  551. if (window.qh.currentCourseIndex > 0) {
  552. console.log('导航到上一课');
  553. const newIndex = window.qh.currentCourseIndex - 1;
  554.  
  555. // 处理iframe嵌套结构
  556. const targetElement = window.qh.courseList[newIndex].element;
  557. if (targetElement.closest('iframe')) {
  558. const iframe = targetElement.closest('iframe');
  559. iframe.contentWindow.postMessage({
  560. type: 'qh-assistant-nav',
  561. index: newIndex,
  562. courseList: window.qh.courseList
  563. }, '*');
  564. }
  565.  
  566. // 添加防抖处理
  567. clearTimeout(window.qh.navigateTimeout);
  568. window.qh.navigateTimeout = setTimeout(() => {
  569. window.qh.currentCourseIndex = newIndex;
  570. navigateToCourse(newIndex);
  571.  
  572. setTimeout(() => {
  573. updateNavButtons();
  574. autoPlayVideo();
  575. }, 1500);
  576. }, 500);
  577. } else {
  578. console.log('已经是第一课,无法导航到上一课');
  579. }
  580. };
  581.  
  582. // 导航到下一课
  583. window.navigateToNextCourse = function() {
  584. // 防止重复触发
  585. if (window.qh.isNavigating && window.qh.lastNavigateTime && (Date.now() - window.qh.lastNavigateTime < 3000)) {
  586. console.log('导航操作过于频繁,忽略本次请求');
  587. return;
  588. }
  589.  
  590. // 记录导航时间
  591. window.qh.lastNavigateTime = Date.now();
  592. window.qh.isNavigating = true;
  593.  
  594. console.log('开始下一课导航流程', { currentIndex: window.qh.currentCourseIndex, courseCount: window.qh.courseList?.length });
  595.  
  596. // 动态更新课程列表
  597. const mainFrame = document.getElementById('taocan_main_frame');
  598. const targetDoc = mainFrame ? mainFrame.contentDocument : document;
  599. collectCourseLinks(targetDoc);
  600.  
  601. if (!window.qh.courseList || window.qh.courseList.length === 0) {
  602. console.error('课程列表未初始化');
  603. updateStatus('课程加载中,请稍候...');
  604. collectCourseLinks(document);
  605. setTimeout(() => navigateToNextCourse(), 2000);
  606. return;
  607. }
  608.  
  609. if (window.qh.currentCourseIndex >= window.qh.courseList.length - 1) {
  610. console.log('已经是最后一课,无法导航到下一课');
  611. updateStatus('已经是最后一课');
  612. return;
  613. }
  614.  
  615. const newIndex = window.qh.currentCourseIndex + 1;
  616. console.log('尝试切换到课程索引:', newIndex, '标题:', window.qh.courseList[newIndex]?.title);
  617.  
  618. // 添加iframe支持
  619. const targetElement = window.qh.courseList[newIndex].element;
  620. if (targetElement.closest('iframe')) {
  621. const iframe = targetElement.closest('iframe');
  622. iframe.contentWindow.postMessage({
  623. type: 'qh-assistant-nav',
  624. index: newIndex,
  625. courseList: window.qh.courseList
  626. }, '*');
  627. }
  628.  
  629. // 添加防抖处理和状态更新
  630. clearTimeout(window.qh.navigateTimeout);
  631. window.qh.navigateTimeout = setTimeout(() => {
  632. window.qh.currentCourseIndex = newIndex;
  633. navigateToCourse(newIndex);
  634.  
  635. setTimeout(() => {
  636. if (window.qh.courseList[newIndex]?.element?.offsetParent === null) {
  637. console.warn('课程元素不可见,重新收集课程列表');
  638. collectCourseLinks(document);
  639. }
  640. updateNavButtons();
  641. autoPlayVideo();
  642.  
  643. // 重置导航状态
  644. setTimeout(() => {
  645. window.qh.isNavigating = false;
  646. console.log('导航状态已重置');
  647. }, 3000);
  648. }, 1500);
  649. }, 500);
  650. };
  651. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址