岐黄天使刷课助手 - 课程导航模块

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

当前为 2025-05-24 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/537075/1594811/%E5%B2%90%E9%BB%84%E5%A4%A9%E4%BD%BF%E5%88%B7%E8%AF%BE%E5%8A%A9%E6%89%8B%20-%20%E8%AF%BE%E7%A8%8B%E5%AF%BC%E8%88%AA%E6%A8%A1%E5%9D%97.js

  1. // ==UserScript==
  2. // @name 岐黄天使刷课助手 - 课程导航模块
  3. // @namespace http://tampermonkey.net/qhtx-modules
  4. // @version 1.3.0
  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. if (!window.qh.panelCreated || !document.getElementById('qh-assistant-panel')) {
  211. // 如果面板未创建,不尝试更新按钮
  212. return;
  213. }
  214.  
  215. const prevBtn = document.getElementById('qh-prev-btn');
  216. const nextBtn = document.getElementById('qh-next-btn');
  217.  
  218. if (prevBtn && nextBtn) {
  219. // 检查课程列表是否有内容
  220. if (!window.qh.courseList || window.qh.courseList.length === 0) {
  221. prevBtn.disabled = true;
  222. nextBtn.disabled = true;
  223. console.log('课程列表为空,禁用导航按钮');
  224. return;
  225. }
  226.  
  227. // 更新按钮状态
  228. prevBtn.disabled = window.qh.currentCourseIndex <= 0;
  229. nextBtn.disabled = window.qh.currentCourseIndex >= window.qh.courseList.length - 1;
  230.  
  231. console.log('更新导航按钮状态:',
  232. '上一课:', prevBtn.disabled ? '禁用' : '启用',
  233. '下一课:', nextBtn.disabled ? '禁用' : '启用',
  234. '当前索引:', window.qh.currentCourseIndex,
  235. '课程总数:', window.qh.courseList.length);
  236.  
  237. // 更新按钮文本,显示上一课/下一课的标题
  238. if (window.qh.currentCourseIndex > 0 && window.qh.courseList[window.qh.currentCourseIndex - 1]) {
  239. const prevTitle = window.qh.courseList[window.qh.currentCourseIndex - 1].title;
  240. prevBtn.title = prevTitle;
  241. // 如果标题太长,截断显示
  242. prevBtn.textContent = '上一课: ' + (prevTitle.length > 10 ? prevTitle.substring(0, 10) + '...' : prevTitle);
  243. } else {
  244. prevBtn.textContent = '上一课';
  245. }
  246.  
  247. if (window.qh.currentCourseIndex < window.qh.courseList.length - 1 && window.qh.courseList[window.qh.currentCourseIndex + 1]) {
  248. const nextTitle = window.qh.courseList[window.qh.currentCourseIndex + 1].title;
  249. nextBtn.title = nextTitle;
  250. // 如果标题太长,截断显示
  251. nextBtn.textContent = '下一课: ' + (nextTitle.length > 10 ? nextTitle.substring(0, 10) + '...' : nextTitle);
  252. } else {
  253. nextBtn.textContent = '下一课';
  254. }
  255.  
  256. // 重新绑定按钮点击事件,确保它们能正常工作
  257. // 先移除旧的事件监听器,避免重复绑定
  258. prevBtn.removeEventListener('click', navigateToPrevCourse);
  259. nextBtn.removeEventListener('click', navigateToNextCourse);
  260.  
  261. // 添加新的事件监听器
  262. prevBtn.addEventListener('click', function() {
  263. console.log('上一课按钮点击,当前索引:', window.qh.currentCourseIndex);
  264. if (window.qh.currentCourseIndex > 0) {
  265. window.qh.currentCourseIndex--;
  266. console.log('新课程索引:', window.qh.currentCourseIndex, '课程标题:', window.qh.courseList[window.qh.currentCourseIndex]?.title);
  267. navigateToCourse(window.qh.currentCourseIndex);
  268. updateNavButtons();
  269. }
  270. });
  271. nextBtn.addEventListener('click', function() {
  272. console.log('下一课按钮点击,当前索引:', window.qh.currentCourseIndex);
  273. if (window.qh.currentCourseIndex < window.qh.courseList.length - 1) {
  274. window.qh.currentCourseIndex++;
  275. console.log('新课程索引:', window.qh.currentCourseIndex, '课程标题:', window.qh.courseList[window.qh.currentCourseIndex]?.title);
  276. navigateToCourse(window.qh.currentCourseIndex);
  277. updateNavButtons();
  278. }
  279. });
  280. } else {
  281. // 按钮不存在,但不在控制台输出警告,避免频繁报错
  282. // 如果面板存在但按钮不存在,可能是DOM结构有问题
  283. if (document.getElementById('qh-assistant-panel')) {
  284. console.debug('面板存在但找不到导航按钮元素,可能需要重新创建面板');
  285. // 考虑在这里重新创建面板或者修复按钮
  286. }
  287. }
  288. };
  289.  
  290. // 获取课程列表,按照DOM顺序排列
  291. function getCourseList(doc) {
  292. try {
  293. // 尝试查找所有可能的课程元素
  294. const courseElements = doc.querySelectorAll('.new_bg[onclick*="getvideoUrl"], .new_bg[onclick*="setti"], .kecheng-item[onclick], li[onclick*="getvideoUrl"], li[onclick*="setti"]');
  295.  
  296. if (courseElements.length === 0) {
  297. console.log('未找到课程元素,尝试查找其他类型的课程元素');
  298. console.log('当前DOM结构:', doc.documentElement.outerHTML.substring(0, 500) + '...');
  299. console.log('尝试查找的课程元素选择器:', '.new_bg[onclick*="getvideoUrl"], .new_bg[onclick*="setti"], .kecheng-item[onclick], li[onclick*="getvideoUrl"], li[onclick*="setti"]');
  300. return [];
  301. }
  302.  
  303. console.log(`找到 ${courseElements.length} 个可能的课程元素`);
  304. if (courseElements.length > 0) {
  305. console.log('第一个课程元素信息:', {
  306. id: courseElements[0].id,
  307. onclick: courseElements[0].getAttribute('onclick'),
  308. classList: Array.from(courseElements[0].classList),
  309. outerHTML: courseElements[0].outerHTML.substring(0, 200) + '...'
  310. });
  311. }
  312. const courses = [];
  313.  
  314. // 创建一个映射来存储课程ID和它们的DOM元素
  315. const courseMap = new Map();
  316.  
  317. courseElements.forEach(item => {
  318. const id = item.id || '';
  319. const onclickAttr = item.getAttribute('onclick') || '';
  320.  
  321. // 提取标题
  322. const titleElement = item.querySelector('.new_biaoti, .title, .name, .course-name');
  323. let title = titleElement ? titleElement.textContent.trim() : '';
  324. // 如果没有找到标题元素,尝试从元素本身获取文本
  325. if (!title && item.textContent) {
  326. title = item.textContent.trim();
  327. }
  328. // 移除"视频"等前缀
  329. title = title.replace(/^(视频|音频|文档)\s*/, '');
  330.  
  331. // 确定状态
  332. const statusElement = item.querySelector('.studystate, .status, .complete, .finished');
  333. const status = (statusElement && (statusElement.textContent.includes('已学完') || statusElement.textContent.includes('已完成')))
  334. || item.classList.contains('finished')
  335. || item.classList.contains('complete')
  336. ? '已完成' : '未完成';
  337.  
  338. // 尝试从onclick属性中提取课程ID和标题
  339. let courseId = id;
  340. let matchFound = false;
  341.  
  342. // 尝试匹配getvideoUrl模式
  343. const getvideoUrlMatch = onclickAttr.match(/getvideoUrl\(([^,]+),\s*['"]([^'"]+)['"]\)/);
  344. if (getvideoUrlMatch) {
  345. courseId = getvideoUrlMatch[1];
  346. const matchTitle = getvideoUrlMatch[2];
  347. if (!title && matchTitle) {
  348. title = matchTitle;
  349. }
  350. matchFound = true;
  351. }
  352.  
  353. // 尝试匹配setti模式
  354. const settiMatch = onclickAttr.match(/setti\(([^)]+)\)/);
  355. if (!matchFound && settiMatch) {
  356. courseId = settiMatch[1];
  357. matchFound = true;
  358. }
  359.  
  360. // 如果找到了匹配,或者onclick属性包含关键字
  361. if (matchFound || onclickAttr.includes('getvideoUrl') || onclickAttr.includes('setti')) {
  362. courseMap.set(courseId, {
  363. id: courseId,
  364. title: title || `课程 ${courseId}`,
  365. element: item,
  366. status: status,
  367. onclickFn: onclickAttr
  368. });
  369. }
  370. });
  371.  
  372. // 获取所有可能的章节容器
  373. const chapterContainers = doc.querySelectorAll('.muli1, .chapter, .section, .module');
  374.  
  375. // 如果找到章节容器,按照DOM顺序遍历章节和课程
  376. if (chapterContainers.length > 0) {
  377. console.log(`找到 ${chapterContainers.length} 个章节容器`);
  378. let index = 0;
  379. chapterContainers.forEach(chapter => {
  380. // 查找章节内的所有课程元素
  381. const courseElementsInChapter = chapter.querySelectorAll('.new_bg[onclick], .kecheng-item[onclick], li[onclick]');
  382. courseElementsInChapter.forEach(element => {
  383. const id = element.id || '';
  384. const onclickAttr = element.getAttribute('onclick') || '';
  385.  
  386. // 尝试从onclick属性中提取课程ID
  387. let courseId = id;
  388.  
  389. // 尝试匹配getvideoUrl模式
  390. const getvideoUrlMatch = onclickAttr.match(/getvideoUrl\(([^,]+),/);
  391. if (getvideoUrlMatch) {
  392. courseId = getvideoUrlMatch[1];
  393. }
  394.  
  395. // 尝试匹配setti模式
  396. const settiMatch = onclickAttr.match(/setti\(([^)]+)\)/);
  397. if (settiMatch) {
  398. courseId = settiMatch[1];
  399. }
  400.  
  401. if (courseMap.has(courseId)) {
  402. const course = courseMap.get(courseId);
  403. course.index = index++;
  404. courses.push(course);
  405. } else if (courseMap.has(id)) {
  406. const course = courseMap.get(id);
  407. course.index = index++;
  408. courses.push(course);
  409. }
  410. });
  411. });
  412. }
  413.  
  414. // 如果通过章节容器没有找到课程,或者找到的课程数量少于总课程数量的一半,
  415. // 则直接按照DOM顺序添加所有课程
  416. if (courses.length === 0 || courses.length < courseMap.size / 2) {
  417. console.log(`通过章节容器只找到 ${courses.length} 个课程,直接按DOM顺序添加所有课程`);
  418. courses.length = 0; // 清空数组
  419. let index = 0;
  420. courseElements.forEach(element => {
  421. const id = element.id || '';
  422. const onclickAttr = element.getAttribute('onclick') || '';
  423.  
  424. // 尝试从onclick属性中提取课程ID
  425. let courseId = id;
  426.  
  427. // 尝试匹配getvideoUrl模式
  428. const getvideoUrlMatch = onclickAttr.match(/getvideoUrl\(([^,]+),/);
  429. if (getvideoUrlMatch) {
  430. courseId = getvideoUrlMatch[1];
  431. }
  432.  
  433. // 尝试匹配setti模式
  434. const settiMatch = onclickAttr.match(/setti\(([^)]+)\)/);
  435. if (settiMatch) {
  436. courseId = settiMatch[1];
  437. }
  438.  
  439. if (courseMap.has(courseId)) {
  440. const course = courseMap.get(courseId);
  441. course.index = index++;
  442. courses.push(course);
  443. } else if (courseMap.has(id)) {
  444. const course = courseMap.get(id);
  445. course.index = index++;
  446. courses.push(course);
  447. }
  448. });
  449. }
  450.  
  451. console.log(`最终找到 ${courses.length} 个课程,按顺序排列`);
  452. if (courses.length > 0) {
  453. console.log('第一个课程详细信息:', {
  454. id: courses[0].id,
  455. title: courses[0].title,
  456. status: courses[0].status,
  457. element: courses[0].element ? courses[0].element.outerHTML.substring(0, 200) + '...' : null
  458. });
  459. }
  460. return courses;
  461. } catch (error) {
  462. console.error('获取课程列表出错:', error);
  463.  
  464. // 如果出错,回退到简单的映射方法
  465. const courseElements = doc.querySelectorAll('.new_bg[onclick], .kecheng-item[onclick], li[onclick*="getvideoUrl"], li[onclick*="setti"]');
  466. return Array.from(courseElements).map((item, index) => {
  467. // 从onclick属性中提取课程ID和标题
  468. const onclickAttr = item.getAttribute('onclick') || '';
  469. const courseId = item.id || '';
  470.  
  471. // 提取标题
  472. const titleElement = item.querySelector('.new_biaoti, .title, .name, .course-name');
  473. let title = titleElement ? titleElement.textContent.trim() : '';
  474. // 如果没有找到标题元素,尝试从元素本身获取文本
  475. if (!title && item.textContent) {
  476. title = item.textContent.trim();
  477. }
  478. // 移除"视频"等前缀
  479. title = title.replace(/^(视频|音频|文档)\s*/, '');
  480.  
  481. // 确定状态
  482. const statusElement = item.querySelector('.studystate, .status, .complete, .finished');
  483. const status = (statusElement && (statusElement.textContent.includes('已学完') || statusElement.textContent.includes('已完成')))
  484. || item.classList.contains('finished')
  485. || item.classList.contains('complete')
  486. ? '已完成' : '未完成';
  487.  
  488. return {
  489. index: index,
  490. id: courseId,
  491. title: title || `课程 ${index + 1}`,
  492. element: item,
  493. status: status,
  494. onclickFn: onclickAttr
  495. };
  496. });
  497. }
  498. }
  499.  
  500. // 导航到指定索引的课程
  501. function navigateToCourse(index) {
  502. console.log('正在切换课程,目标索引:', index);
  503. try {
  504. if (index < 0 || index >= window.qh.courseList.length) {
  505. console.error('无效的课程索引:', index);
  506. return;
  507. }
  508. const targetCourse = window.qh.courseList[index];
  509. if (targetCourse?.element) {
  510. console.log('正在跳转至课程:', targetCourse.title);
  511. targetCourse.element.click();
  512. // 添加防抖处理防止重复点击
  513. setTimeout(() => updateNavButtons(), 500);
  514. }
  515. } catch (e) {
  516. console.error('课程切换失败:', e);
  517. }
  518. }
  519.  
  520. // 导航到上一课
  521. window.navigateToPrevCourse = function() {
  522. // 动态更新课程列表
  523. const mainFrame = document.getElementById('taocan_main_frame');
  524. const targetDoc = mainFrame ? mainFrame.contentDocument : document;
  525. collectCourseLinks(targetDoc);
  526.  
  527. if (window.qh.currentCourseIndex > 0) {
  528. console.log('导航到上一课');
  529. const newIndex = window.qh.currentCourseIndex - 1;
  530.  
  531. // 处理iframe嵌套结构
  532. const targetElement = window.qh.courseList[newIndex].element;
  533. if (targetElement.closest('iframe')) {
  534. const iframe = targetElement.closest('iframe');
  535. iframe.contentWindow.postMessage({
  536. type: 'qh-assistant-nav',
  537. index: newIndex,
  538. courseList: window.qh.courseList
  539. }, '*');
  540. }
  541.  
  542. // 添加防抖处理
  543. clearTimeout(window.qh.navigateTimeout);
  544. window.qh.navigateTimeout = setTimeout(() => {
  545. window.qh.currentCourseIndex = newIndex;
  546. navigateToCourse(newIndex);
  547.  
  548. setTimeout(() => {
  549. updateNavButtons();
  550. autoPlayVideo();
  551. }, 1500);
  552. }, 500);
  553. } else {
  554. console.log('已经是第一课,无法导航到上一课');
  555. }
  556. };
  557.  
  558. // 导航到下一课
  559. window.navigateToNextCourse = function() {
  560. // 防止重复触发
  561. if (window.qh.isNavigating && window.qh.lastNavigateTime && (Date.now() - window.qh.lastNavigateTime < 3000)) {
  562. console.log('导航操作过于频繁,忽略本次请求');
  563. return;
  564. }
  565.  
  566. // 记录导航时间
  567. window.qh.lastNavigateTime = Date.now();
  568. window.qh.isNavigating = true;
  569.  
  570. console.log('开始下一课导航流程', { currentIndex: window.qh.currentCourseIndex, courseCount: window.qh.courseList?.length });
  571.  
  572. // 动态更新课程列表
  573. const mainFrame = document.getElementById('taocan_main_frame');
  574. const targetDoc = mainFrame ? mainFrame.contentDocument : document;
  575. collectCourseLinks(targetDoc);
  576.  
  577. if (!window.qh.courseList || window.qh.courseList.length === 0) {
  578. console.error('课程列表未初始化');
  579. updateStatus('课程加载中,请稍候...');
  580. collectCourseLinks(document);
  581. setTimeout(() => navigateToNextCourse(), 2000);
  582. return;
  583. }
  584.  
  585. if (window.qh.currentCourseIndex >= window.qh.courseList.length - 1) {
  586. console.log('已经是最后一课,无法导航到下一课');
  587. updateStatus('已经是最后一课');
  588. return;
  589. }
  590.  
  591. const newIndex = window.qh.currentCourseIndex + 1;
  592. console.log('尝试切换到课程索引:', newIndex, '标题:', window.qh.courseList[newIndex]?.title);
  593.  
  594. // 添加iframe支持
  595. const targetElement = window.qh.courseList[newIndex].element;
  596. if (targetElement.closest('iframe')) {
  597. const iframe = targetElement.closest('iframe');
  598. iframe.contentWindow.postMessage({
  599. type: 'qh-assistant-nav',
  600. index: newIndex,
  601. courseList: window.qh.courseList
  602. }, '*');
  603. }
  604.  
  605. // 添加防抖处理和状态更新
  606. clearTimeout(window.qh.navigateTimeout);
  607. window.qh.navigateTimeout = setTimeout(() => {
  608. window.qh.currentCourseIndex = newIndex;
  609. navigateToCourse(newIndex);
  610.  
  611. setTimeout(() => {
  612. if (window.qh.courseList[newIndex]?.element?.offsetParent === null) {
  613. console.warn('课程元素不可见,重新收集课程列表');
  614. collectCourseLinks(document);
  615. }
  616. updateNavButtons();
  617. autoPlayVideo();
  618.  
  619. // 重置导航状态
  620. setTimeout(() => {
  621. window.qh.isNavigating = false;
  622. console.log('导航状态已重置');
  623. }, 3000);
  624. }, 1500);
  625. }, 500);
  626. };
  627. })();

QingJ © 2025

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